diff --git a/DEPS b/DEPS index 0b9c4a4..3363f0e 100644 --- a/DEPS +++ b/DEPS
@@ -40,11 +40,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '9206c76337cd349c769103e06af005edd0583a10', + 'skia_revision': '63afe64a5f33a61235706dbec0f0cc695c88469b', # 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': '5e864d9b912f3d6a1f322f61ed434199cb02e73a', + 'v8_revision': '3ed8063bc7c7c2e5081ef65ebd5bede0715733b6', # 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. @@ -64,7 +64,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': '576e8151efab01166142ec697b66ce38b7bf6780', + 'pdfium_revision': '00d4064e5414fc0845e354b50c7f1a8323449268', # 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. @@ -96,7 +96,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': '6bc0354c35405b02db027aa20976c14983d21955', + 'catapult_revision': '2c3f1f3d69c01170e93ab32a441b51b1712016c1', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other.
diff --git a/android_webview/browser/aw_browser_terminator.cc b/android_webview/browser/aw_browser_terminator.cc index 34052e0..d34f5f24 100644 --- a/android_webview/browser/aw_browser_terminator.cc +++ b/android_webview/browser/aw_browser_terminator.cc
@@ -67,9 +67,18 @@ GetAwRenderProcessGoneDelegatesForRenderProcess(child_process_id, &delegates); for (auto delegate : delegates) { if (!delegate->OnRenderProcessGoneDetail(child_process_id, crashed)) { - // Keeps this log unchanged, CTS test uses it to detect crash. - LOG(FATAL) << "Render process's abnormal termination wasn't handled by" - << " all associated webviews, triggering application crash"; + if (crashed) { + // Keeps this log unchanged, CTS test uses it to detect crash. + LOG(FATAL) << "Render process's crash wasn't handled by all associated" + << " webviews, triggering application crash"; + } else { + // The render process was most likely killed for OOM or switching + // WebView provider, to make WebView backward compatible, kills the + // browser process instead of triggering crash. + LOG(ERROR) << "Render process kill (OOM or update) wasn't handed by" + << " all associated webviews, killing application."; + kill(getpid(), SIGKILL); + } } } }
diff --git a/android_webview/browser/net/aw_url_request_context_getter.cc b/android_webview/browser/net/aw_url_request_context_getter.cc index 8b8977b..8bd349a 100644 --- a/android_webview/browser/net/aw_url_request_context_getter.cc +++ b/android_webview/browser/net/aw_url_request_context_getter.cc
@@ -23,7 +23,6 @@ #include "base/memory/ptr_util.h" #include "base/strings/string_number_conversions.h" #include "base/threading/sequenced_worker_pool.h" -#include "base/threading/worker_pool.h" #include "components/prefs/pref_service.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" @@ -219,8 +218,7 @@ BrowserThread::GetBlockingPool()->GetSequenceToken())); channel_id_service.reset(new net::ChannelIDService( - new net::DefaultChannelIDStore(channel_id_db.get()), - base::WorkerPool::GetTaskRunner(true))); + new net::DefaultChannelIDStore(channel_id_db.get()))); } // Android provides a local HTTP proxy that handles all the proxying.
diff --git a/android_webview/lib/main/webview_entry_point.cc b/android_webview/lib/main/webview_entry_point.cc index dfda801..c13e331 100644 --- a/android_webview/lib/main/webview_entry_point.cc +++ b/android_webview/lib/main/webview_entry_point.cc
@@ -12,7 +12,7 @@ // WebView uses native JNI exports; disable manual JNI registration because // we don't have a good way to detect the JNI registrations which is called, // outside of OnJNIOnLoadRegisterJNI code path. - base::android::DisableManualJniRegistration(); + base::android::SetJniRegistrationType(base::android::NO_JNI_REGISTRATION); base::android::InitVM(vm); base::android::SetNativeInitializationHook(&android_webview::OnJNIOnLoadInit); return JNI_VERSION_1_4;
diff --git a/android_webview/renderer/aw_content_renderer_client.cc b/android_webview/renderer/aw_content_renderer_client.cc index 931fe2ea..81443c6d4 100644 --- a/android_webview/renderer/aw_content_renderer_client.cc +++ b/android_webview/renderer/aw_content_renderer_client.cc
@@ -136,7 +136,7 @@ } bool ignore_navigation = false; - base::string16 url = request.url().string(); + base::string16 url = request.url().string().utf16(); bool has_user_gesture = request.hasUserGesture(); int render_frame_id = render_frame->GetRoutingID(); @@ -195,8 +195,8 @@ if (error_html) { GURL gurl(failed_request.url()); std::string url = net::EscapeForHTML(gurl.possibly_invalid_spec()); - std::string err = - base::UTF16ToUTF8(base::StringPiece16(error.localizedDescription)); + std::string err = error.localizedDescription.utf8( + blink::WebString::UTF8ConversionMode::kStrictReplacingErrorsWithFFFD); std::vector<std::string> replacements; replacements.push_back( @@ -241,7 +241,7 @@ if (error.localizedDescription.isEmpty()) *error_description = base::ASCIIToUTF16(net::ErrorToString(error.reason)); else - *error_description = error.localizedDescription; + *error_description = error.localizedDescription.utf16(); } }
diff --git a/android_webview/renderer/aw_render_frame_ext.cc b/android_webview/renderer/aw_render_frame_ext.cc index 657dfba6..8c59dfd3 100644 --- a/android_webview/renderer/aw_render_frame_ext.cc +++ b/android_webview/renderer/aw_render_frame_ext.cc
@@ -32,19 +32,20 @@ GURL GetAbsoluteUrl(const blink::WebNode& node, const base::string16& url_fragment) { - return GURL(node.document().completeURL(url_fragment)); + return GURL( + node.document().completeURL(blink::WebString::fromUTF16(url_fragment))); } base::string16 GetHref(const blink::WebElement& element) { // Get the actual 'href' attribute, which might relative if valid or can // possibly contain garbage otherwise, so not using absoluteLinkURL here. - return element.getAttribute("href"); + return element.getAttribute("href").utf16(); } GURL GetAbsoluteSrcUrl(const blink::WebElement& element) { if (element.isNull()) return GURL(); - return GetAbsoluteUrl(element, element.getAttribute("src")); + return GetAbsoluteUrl(element, element.getAttribute("src").utf16()); } blink::WebElement GetImgChild(const blink::WebNode& node) { @@ -201,7 +202,7 @@ AwHitTestData data; data.href = GetHref(element); - data.anchor_text = element.textContent(); + data.anchor_text = element.textContent().utf16(); GURL absolute_link_url; if (node.isLink()) @@ -230,7 +231,7 @@ GURL absolute_image_url = result.absoluteImageURL(); if (!result.urlElement().isNull()) { - data.anchor_text = result.urlElement().textContent(); + data.anchor_text = result.urlElement().textContent().utf16(); data.href = GetHref(result.urlElement()); // If we hit an image that failed to load, Blink won't give us its URL. // Fall back to walking the DOM in this case.
diff --git a/ash/ash_chromeos_strings.grdp b/ash/ash_chromeos_strings.grdp index 0836b42f..c09d75f 100644 --- a/ash/ash_chromeos_strings.grdp +++ b/ash/ash_chromeos_strings.grdp
@@ -292,9 +292,15 @@ <message name="IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING" desc="The label used in the tray to show that the current status is mirroring."> Mirroring to <ph name="DISPLAY_NAME">$1</ph> </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_MIRROR_EXIT" desc="The label used in the tray to show that mirror mode is exiting."> + Exiting mirror mode + </message> <message name="IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED" desc="The label used in the tray to show that the current status is extended."> Extending screen to <ph name="DISPLAY_NAME">$1</ph> </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_REMOVED" desc="The text of the notification to show when a display is removed."> + Removed display <ph name="DISPLAY_NAME">$1</ph> + </message> <message name="IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING_NO_INTERNAL" desc="The label used in the tray to show that the current status is mirroring and the device doesn't have the internal display."> Mirroring </message> @@ -307,6 +313,9 @@ <message name="IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED_DESCRIPTION" desc="The body of the notification indicating that the system is in docked mode."> You can keep using your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> while it's connected to an external display, even with the lid closed. </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED_EXITING" desc="The notification message shown to the user when the device is exiting docked mode."> + Exiting docked mode + </message> <message name="IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED" desc="The label used in the tray to notify that the display resolution settings has changed."> <ph name="DISPLAY_NAME">$1</ph> resolution was changed to <ph name="RESOLUTION">$2</ph> </message> @@ -337,6 +346,9 @@ <message name="IDS_ASH_STATUS_TRAY_DISPLAY_UNIFIED" desc="The title of the notification indicating that the system is in unified desktop mode."> Unified desktop mode </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_UNIFIED_EXITING" desc="The title of the notification indicating that the system is exiting unified desktop mode."> + Exiting unified desktop mode + </message> <message name="IDS_ASH_STATUS_TRAY_CAPS_LOCK_ENABLED" desc="The label used for the tray item to indicate caps lock is on and to toggle caps lock by the click."> CAPS LOCK is on </message>
diff --git a/ash/system/chromeos/screen_layout_observer.cc b/ash/system/chromeos/screen_layout_observer.cc index 6140a20f..d3daf31 100644 --- a/ash/system/chromeos/screen_layout_observer.cc +++ b/ash/system/chromeos/screen_layout_observer.cc
@@ -101,35 +101,29 @@ } } -// Returns the name of the currently connected external display. This should not -// be used when the external display is used for mirroring. -base::string16 GetExternalDisplayName() { +// Returns the name of the currently connected external display whose ID is +// |external_display_id|. This should not be used when the external display is +// used for mirroring. +base::string16 GetExternalDisplayName(int64_t external_display_id) { + DCHECK(!display::Display::IsInternalDisplayId(external_display_id)); + display::DisplayManager* display_manager = GetDisplayManager(); DCHECK(!display_manager->IsInMirrorMode()); - int64_t external_id = display::kInvalidDisplayId; - for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) { - int64_t id = display_manager->GetDisplayAt(i).id(); - if (!display::Display::IsInternalDisplayId(id)) { - external_id = id; - break; - } - } - - if (external_id == display::kInvalidDisplayId) + if (external_display_id == display::kInvalidDisplayId) return l10n_util::GetStringUTF16(IDS_DISPLAY_NAME_UNKNOWN); // The external display name may have an annotation of "(width x height)" in // case that the display is rotated or its resolution is changed. - base::string16 name = GetDisplayName(external_id); + base::string16 name = GetDisplayName(external_display_id); const display::ManagedDisplayInfo& display_info = - display_manager->GetDisplayInfo(external_id); + display_manager->GetDisplayInfo(external_display_id); if (display_info.GetActiveRotation() != display::Display::ROTATE_0 || display_info.configured_ui_scale() != 1.0f || !display_info.overscan_insets_in_dip().IsEmpty()) { name = l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATED_NAME, - name, GetDisplaySize(external_id)); + name, GetDisplaySize(external_display_id)); } else if (display_info.overscan_insets_in_dip().IsEmpty() && display_info.has_overscan()) { name = l10n_util::GetStringFUTF16( @@ -141,41 +135,77 @@ return name; } -base::string16 GetDisplayMessage(base::string16* additional_message_out) { +// Returns true if docked mode is currently enabled. +bool IsDockedModeEnabled() { display::DisplayManager* display_manager = GetDisplayManager(); - if (display_manager->GetNumDisplays() > 1) { - if (display::Display::HasInternalDisplay()) { - return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED, - GetExternalDisplayName()); + if (!display::Display::HasInternalDisplay()) + return false; + + for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) { + if (display::Display::IsInternalDisplayId( + display_manager->GetDisplayAt(i).id())) { + return false; } + } + + // We have an internal display but it's not one of the active displays. + return true; +} + +// Returns the notification message that should be shown to the user when the +// docked mode is entered. +base::string16 GetDockedModeEnabledMessage( + base::string16* out_additional_message) { + DCHECK(IsDockedModeEnabled()); + DCHECK(out_additional_message); + + *out_additional_message = ash::SubstituteChromeOSDeviceType( + IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED_DESCRIPTION); + return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED); +} + +// Returns the notification message that should be shown when mirror display +// mode is entered. +base::string16 GetEnterMirrorModeMessage() { + if (display::Display::HasInternalDisplay()) { + return l10n_util::GetStringFUTF16( + IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, + GetDisplayName(GetDisplayManager()->mirroring_display_id())); + } + + return l10n_util::GetStringUTF16( + IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING_NO_INTERNAL); +} + +// Returns the notification message that should be shown when unified desktop +// mode is entered. +base::string16 GetEnterUnifiedModeMessage() { + return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_UNIFIED); +} + +// Returns the notification message that should be shown when unified desktop +// mode is exited. +base::string16 GetExitUnifiedModeMessage() { + return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_UNIFIED_EXITING); +} + +base::string16 GetDisplayRemovedMessage( + const display::ManagedDisplayInfo& removed_display_info, + base::string16* out_additional_message) { + return l10n_util::GetStringFUTF16( + IDS_ASH_STATUS_TRAY_DISPLAY_REMOVED, + base::UTF8ToUTF16(removed_display_info.name())); +} + +base::string16 GetDisplayAddedMessage(int64_t added_display_id, + base::string16* additional_message_out) { + if (!display::Display::HasInternalDisplay()) { return l10n_util::GetStringUTF16( IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL); } - if (display_manager->IsInMirrorMode()) { - if (display::Display::HasInternalDisplay()) { - return l10n_util::GetStringFUTF16( - IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, - GetDisplayName(display_manager->mirroring_display_id())); - } - return l10n_util::GetStringUTF16( - IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING_NO_INTERNAL); - } - - if (display_manager->IsInUnifiedMode()) - return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_UNIFIED); - - int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); - if (display::Display::HasInternalDisplay() && - !(display::Display::IsInternalDisplayId(primary_id))) { - if (additional_message_out) { - *additional_message_out = ash::SubstituteChromeOSDeviceType( - IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED_DESCRIPTION); - } - return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED); - } - - return base::string16(); + return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED, + GetExternalDisplayName(added_display_id)); } } // namespace @@ -207,37 +237,84 @@ bool ScreenLayoutObserver::GetDisplayMessageForNotification( const ScreenLayoutObserver::DisplayInfoMap& old_info, - base::string16* message_out, - base::string16* additional_message_out) { - // Display is added or removed. Use the same message as the one in - // the system tray. - if (display_info_.size() != old_info.size()) { - *message_out = GetDisplayMessage(additional_message_out); - return true; + base::string16* out_message, + base::string16* out_additional_message) { + if (old_display_mode_ != current_display_mode_) { + // Detect changes in the mirror mode status. + if (current_display_mode_ == DisplayMode::MIRRORING) { + *out_message = GetEnterMirrorModeMessage(); + return true; + } + if (old_display_mode_ == DisplayMode::MIRRORING && + GetExitMirrorModeMessage(out_message, out_additional_message)) { + return true; + } + + // Detect changes in the unified mode status. + if (current_display_mode_ == DisplayMode::UNIFIED) { + *out_message = GetEnterUnifiedModeMessage(); + return true; + } + if (old_display_mode_ == DisplayMode::UNIFIED) { + *out_message = GetExitUnifiedModeMessage(); + return true; + } + + if (current_display_mode_ == DisplayMode::DOCKED) { + *out_message = GetDockedModeEnabledMessage(out_additional_message); + return true; + } + if (old_display_mode_ == DisplayMode::DOCKED) { + *out_message = + l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED_EXITING); + return true; + } } - for (DisplayInfoMap::const_iterator iter = display_info_.begin(); - iter != display_info_.end(); ++iter) { - DisplayInfoMap::const_iterator old_iter = old_info.find(iter->first); - // The display's number is same but different displays. This happens - // for the transition between docked mode and mirrored display. Falls back - // to GetDisplayMessage(). - if (old_iter == old_info.end()) { - *message_out = GetDisplayMessage(additional_message_out); + // Displays are added or removed. + if (display_info_.size() < old_info.size()) { + // A display has been removed. + for (const auto& iter : old_info) { + if (display_info_.count(iter.first)) + continue; + + *out_message = + GetDisplayRemovedMessage(iter.second, out_additional_message); return true; } + } else if (display_info_.size() > old_info.size()) { + // A display has been added. + for (const auto& iter : display_info_) { + if (old_info.count(iter.first)) + continue; + + *out_message = GetDisplayAddedMessage(iter.first, out_additional_message); + return true; + } + } + + for (const auto& iter : display_info_) { + DisplayInfoMap::const_iterator old_iter = old_info.find(iter.first); + if (old_iter == old_info.end()) { + // The display's number is same but different displays. This happens + // for the transition between docked mode and mirrored display. + // This condition can never be reached here, since it is handled above. + NOTREACHED() << "A display mode transition that should have been handled" + "earlier."; + return false; + } - if (iter->second.configured_ui_scale() != + if (iter.second.configured_ui_scale() != old_iter->second.configured_ui_scale()) { - *additional_message_out = l10n_util::GetStringFUTF16( + *out_additional_message = l10n_util::GetStringFUTF16( IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED, - GetDisplayName(iter->first), GetDisplaySize(iter->first)); + GetDisplayName(iter.first), GetDisplaySize(iter.first)); return true; } - if (iter->second.GetActiveRotation() != + if (iter.second.GetActiveRotation() != old_iter->second.GetActiveRotation()) { int rotation_text_id = 0; - switch (iter->second.GetActiveRotation()) { + switch (iter.second.GetActiveRotation()) { case display::Display::ROTATE_0: rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_STANDARD_ORIENTATION; break; @@ -251,8 +328,8 @@ rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270; break; } - *additional_message_out = l10n_util::GetStringFUTF16( - IDS_ASH_STATUS_TRAY_DISPLAY_ROTATED, GetDisplayName(iter->first), + *out_additional_message = l10n_util::GetStringFUTF16( + IDS_ASH_STATUS_TRAY_DISPLAY_ROTATED, GetDisplayName(iter.first), l10n_util::GetStringUTF16(rotation_text_id)); return true; } @@ -303,6 +380,20 @@ DisplayInfoMap old_info; UpdateDisplayInfo(&old_info); + old_display_mode_ = current_display_mode_; + if (GetDisplayManager()->IsInMirrorMode()) + current_display_mode_ = DisplayMode::MIRRORING; + else if (GetDisplayManager()->IsInUnifiedMode()) + current_display_mode_ = DisplayMode::UNIFIED; + else if (IsDockedModeEnabled()) + current_display_mode_ = DisplayMode::DOCKED; + else if (GetDisplayManager()->GetNumDisplays() > 2) + current_display_mode_ = DisplayMode::EXTENDED_3_PLUS; + else if (GetDisplayManager()->GetNumDisplays() == 2) + current_display_mode_ = DisplayMode::EXTENDED_2; + else + current_display_mode_ = DisplayMode::SINGLE; + if (!show_notifications_for_testing) return; @@ -312,4 +403,39 @@ CreateOrUpdateNotification(message, additional_message); } +bool ScreenLayoutObserver::GetExitMirrorModeMessage( + base::string16* out_message, + base::string16* out_additional_message) { + switch (current_display_mode_) { + case DisplayMode::DOCKED: + // Handle disabling mirror mode as a result of going to docked mode + // when we only have a single display (this means we actually have two + // physical displays, one of which is the internal display, but they + // were in mirror mode, and hence considered as one. Closing the + // internal display disables mirror mode and we still have a single + // active display). + *out_message = GetDockedModeEnabledMessage(out_additional_message); + return true; + + case DisplayMode::EXTENDED_3_PLUS: + // Mirror mode was turned off due to having more than two displays. + // Show a message that mirror mode for 3+ displays is not supported. + *out_message = + l10n_util::GetStringUTF16(IDS_ASH_DISPLAY_MIRRORING_NOT_SUPPORTED); + return true; + + case DisplayMode::SINGLE: + // We're exiting mirror mode because we removed one of the two + // displays. + *out_message = + l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_MIRROR_EXIT); + return true; + + default: + // Mirror mode was turned off; other messages should be shown e.g. + // extended mode is on, ... etc. + return false; + } +} + } // namespace ash
diff --git a/ash/system/chromeos/screen_layout_observer.h b/ash/system/chromeos/screen_layout_observer.h index 3324f7c..1ef8bbb 100644 --- a/ash/system/chromeos/screen_layout_observer.h +++ b/ash/system/chromeos/screen_layout_observer.h
@@ -51,15 +51,32 @@ // also sets |additional_message_out| which appears in the notification with // the |message_out|. bool GetDisplayMessageForNotification(const DisplayInfoMap& old_info, - base::string16* message_out, - base::string16* additional_message_out); + base::string16* out_message, + base::string16* out_additional_message); // Creates or updates the display notification. void CreateOrUpdateNotification(const base::string16& message, const base::string16& additional_message); + // Returns the notification message that should be shown when mirror display + // mode is exited. + bool GetExitMirrorModeMessage(base::string16* out_message, + base::string16* out_additional_message); + DisplayInfoMap display_info_; + enum class DisplayMode { + SINGLE, + EXTENDED_2, // 2 displays in extended mode. + EXTENDED_3_PLUS, // 3+ displays in extended mode. + MIRRORING, + UNIFIED, + DOCKED + }; + + DisplayMode old_display_mode_ = DisplayMode::SINGLE; + DisplayMode current_display_mode_ = DisplayMode::SINGLE; + bool show_notifications_for_testing = true; DISALLOW_COPY_AND_ASSIGN(ScreenLayoutObserver);
diff --git a/ash/system/chromeos/screen_layout_observer_unittest.cc b/ash/system/chromeos/screen_layout_observer_unittest.cc index 25346d0f..8d6477f 100644 --- a/ash/system/chromeos/screen_layout_observer_unittest.cc +++ b/ash/system/chromeos/screen_layout_observer_unittest.cc
@@ -16,6 +16,7 @@ #include "ui/accessibility/ax_node_data.h" #include "ui/base/l10n/l10n_util.h" #include "ui/display/display.h" +#include "ui/display/display_layout_builder.h" #include "ui/display/manager/display_manager.h" #include "ui/message_center/message_center.h" #include "ui/message_center/notification.h" @@ -93,14 +94,12 @@ ScreenLayoutObserverTest::GetDisplayNotification() const { 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() == ScreenLayoutObserver::kNotificationId) - return *iter; + for (const auto& notification : notifications) { + if (notification->id() == ScreenLayoutObserver::kNotificationId) + return notification; } - return NULL; + return nullptr; } TEST_F(ScreenLayoutObserverTest, DisplayNotifications) { @@ -228,10 +227,13 @@ IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL), GetDisplayNotificationText()); - // Back to the single display. It SHOULD remove the notification since the - // information is stale. + // Back to the single display. It should show that a display was removed. UpdateDisplay("400x400"); - EXPECT_TRUE(GetDisplayNotificationText().empty()); + EXPECT_TRUE(base::StartsWith( + GetDisplayNotificationText(), + l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_REMOVED, + base::UTF8ToUTF16("")), + base::CompareCase::SENSITIVE)); } // Verify the notification message content when one of the 2 displays that @@ -272,4 +274,180 @@ EXPECT_TRUE(GetDisplayNotificationAdditionalText().empty()); } +// Tests that exiting mirror mode by closing the lid shows the correct "docked +// mode" message. +TEST_F(ScreenLayoutObserverTest, ExitMirrorModeBecauseOfDockedModeMessage) { + Shell::GetInstance() + ->screen_layout_observer() + ->set_show_notifications_for_testing(true); + UpdateDisplay("400x400,200x200"); + display::Display::SetInternalDisplayId( + display_manager()->GetSecondaryDisplay().id()); + + // Mirroring. + display_manager()->SetSoftwareMirroring(true); + UpdateDisplay("400x400,200x200"); + EXPECT_EQ(l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, + GetMirroringDisplayName()), + GetDisplayNotificationText()); + + // Docked. + CloseNotification(); + display_manager()->SetSoftwareMirroring(false); + display::Display::SetInternalDisplayId(display_manager()->first_display_id()); + UpdateDisplay("200x200"); + EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED), + GetDisplayNotificationText()); + EXPECT_EQ(ash::SubstituteChromeOSDeviceType( + IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED_DESCRIPTION), + GetDisplayNotificationAdditionalText()); +} + +// Tests that exiting mirror mode because of adding a third display shows the +// correct "3+ displays mirror mode is not supported" message. +TEST_F(ScreenLayoutObserverTest, ExitMirrorModeBecauseOfThirdDisplayMessage) { + Shell::GetInstance() + ->screen_layout_observer() + ->set_show_notifications_for_testing(true); + UpdateDisplay("400x400,200x200"); + display::Display::SetInternalDisplayId( + display_manager()->GetSecondaryDisplay().id()); + + // Mirroring. + display_manager()->SetSoftwareMirroring(true); + UpdateDisplay("400x400,200x200"); + EXPECT_EQ(l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, + GetMirroringDisplayName()), + GetDisplayNotificationText()); + + // Adding a third display. Mirror mode for 3+ displays is not supported. + CloseNotification(); + display_manager()->SetSoftwareMirroring(false); + UpdateDisplay("400x400,200x200,100x100"); + EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_DISPLAY_MIRRORING_NOT_SUPPORTED), + GetDisplayNotificationText()); +} + +// Special case: tests that exiting mirror mode by removing a display shows the +// correct message. +TEST_F(ScreenLayoutObserverTest, + ExitMirrorModeNoInternalDisplayBecauseOfDisplayRemovedMessage) { + Shell::GetInstance() + ->screen_layout_observer() + ->set_show_notifications_for_testing(true); + UpdateDisplay("400x400,200x200"); + display::Display::SetInternalDisplayId( + display_manager()->GetSecondaryDisplay().id()); + + // Mirroring. + display_manager()->SetSoftwareMirroring(true); + UpdateDisplay("400x400,200x200"); + EXPECT_EQ(l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, + GetMirroringDisplayName()), + GetDisplayNotificationText()); + + // Removing one of the displays. We show that we exited mirror mode. + CloseNotification(); + display_manager()->SetSoftwareMirroring(false); + UpdateDisplay("400x400"); + EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_MIRROR_EXIT), + GetDisplayNotificationText()); +} + +// Tests notification messages shown when adding and removing displays in +// extended mode. +TEST_F(ScreenLayoutObserverTest, AddingRemovingDisplayExtendedModeMessage) { + Shell::GetInstance() + ->screen_layout_observer() + ->set_show_notifications_for_testing(true); + UpdateDisplay("400x400"); + EXPECT_TRUE(GetDisplayNotificationText().empty()); + + // Adding a display in extended mode. + UpdateDisplay("400x400,200x200"); + EXPECT_EQ(l10n_util::GetStringUTF16( + IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL), + GetDisplayNotificationText()); + + // Removing a display. + CloseNotification(); + UpdateDisplay("400x400"); + EXPECT_TRUE(base::StartsWith( + GetDisplayNotificationText(), + l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_REMOVED, + base::UTF8ToUTF16("")), + base::CompareCase::SENSITIVE)); +} + +// Tests notification messages shown when entering and exiting unified desktop +// mode. +TEST_F(ScreenLayoutObserverTest, EnteringExitingUnifiedModeMessage) { + Shell::GetInstance() + ->screen_layout_observer() + ->set_show_notifications_for_testing(true); + UpdateDisplay("400x400"); + EXPECT_TRUE(GetDisplayNotificationText().empty()); + + // Adding a display in extended mode. + UpdateDisplay("400x400,200x200"); + EXPECT_EQ(l10n_util::GetStringUTF16( + IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL), + GetDisplayNotificationText()); + + // Enter unified mode. + display_manager()->SetUnifiedDesktopEnabled(true); + EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_UNIFIED), + GetDisplayNotificationText()); + + // Exit unified mode. + display_manager()->SetUnifiedDesktopEnabled(false); + EXPECT_EQ( + l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_UNIFIED_EXITING), + GetDisplayNotificationText()); + + // Enter unified mode again and exit via closing the lid. The message "Exiting + // unified mode" should be shown. + display_manager()->SetUnifiedDesktopEnabled(true); + EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_UNIFIED), + GetDisplayNotificationText()); + + // Close the lid. + display::Display::SetInternalDisplayId(display_manager()->first_display_id()); + UpdateDisplay("200x200"); + display_manager()->SetUnifiedDesktopEnabled(false); + EXPECT_EQ( + l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_UNIFIED_EXITING), + GetDisplayNotificationText()); +} + +// Special case: Tests notification messages shown when entering docked mode +// by closing the lid and the internal display is the secondary display. +TEST_F(ScreenLayoutObserverTest, DockedModeWithExternalPrimaryDisplayMessage) { + Shell::GetInstance() + ->screen_layout_observer() + ->set_show_notifications_for_testing(true); + UpdateDisplay("400x400,200x200"); + EXPECT_EQ(l10n_util::GetStringUTF16( + IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL), + GetDisplayNotificationText()); + CloseNotification(); + + const int64_t primary_id = display_manager()->GetDisplayAt(0).id(); + const int64_t internal_secondary_id = display_manager()->GetDisplayAt(1).id(); + display::Display::SetInternalDisplayId(internal_secondary_id); + display::DisplayLayoutBuilder builder(primary_id); + builder.AddDisplayPlacement(internal_secondary_id, primary_id, + display::DisplayPlacement::LEFT, 0); + display_manager()->SetLayoutForCurrentDisplays(builder.Build()); + EXPECT_TRUE(GetDisplayNotificationText().empty()); + + // Close the lid and expect we show the docked mode message. + UpdateDisplay("400x400"); + EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED), + GetDisplayNotificationText()); + EXPECT_EQ(ash::SubstituteChromeOSDeviceType( + IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED_DESCRIPTION), + GetDisplayNotificationAdditionalText()); +} + } // namespace ash
diff --git a/ash/wm/window_cycle_event_filter_aura.cc b/ash/wm/window_cycle_event_filter_aura.cc index a8dadb9..77ec193 100644 --- a/ash/wm/window_cycle_event_filter_aura.cc +++ b/ash/wm/window_cycle_event_filter_aura.cc
@@ -4,6 +4,7 @@ #include "ash/wm/window_cycle_event_filter_aura.h" +#include "ash/common/accelerators/debug_commands.h" #include "ash/common/wm/window_cycle_controller.h" #include "ash/common/wm/window_cycle_list.h" #include "ash/common/wm_shell.h" @@ -27,14 +28,15 @@ } void WindowCycleEventFilterAura::OnKeyEvent(ui::KeyEvent* event) { - // Until the alt key is released, all key events except the tab press (which - // is handled by the accelerator controller to call Step) are handled by this - // window cycle controller: https://crbug.com/340339. - if (event->key_code() != ui::VKEY_TAB || - event->type() != ui::ET_KEY_PRESSED) { + // Until the alt key is released, all key events except the trigger key press + // (which is handled by the accelerator controller to call Step) are handled + // by this window cycle controller: https://crbug.com/340339. + bool is_trigger_key = event->key_code() == ui::VKEY_TAB || + (debug::DeveloperAcceleratorsEnabled() && + event->key_code() == ui::VKEY_W); + if (!is_trigger_key || event->type() != ui::ET_KEY_PRESSED) event->StopPropagation(); - } - if (event->key_code() == ui::VKEY_TAB) { + if (is_trigger_key) { if (event->type() == ui::ET_KEY_RELEASED) { repeat_timer_.Stop(); } else if (event->type() == ui::ET_KEY_PRESSED && event->is_repeat() &&
diff --git a/base/android/application_status_listener_unittest.cc b/base/android/application_status_listener_unittest.cc index 803dedb..a6f098b 100644 --- a/base/android/application_status_listener_unittest.cc +++ b/base/android/application_status_listener_unittest.cc
@@ -69,6 +69,9 @@ APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES); event_.Wait(); EXPECT_EQ(APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES, state_); + + // Delete |listener_| on the thread on which it was created. + thread_.task_runner()->DeleteSoon(FROM_HERE, listener_.release()); } private:
diff --git a/base/android/java/src/org/chromium/base/JNIUtils.java b/base/android/java/src/org/chromium/base/JNIUtils.java index f971b5e0..3fcec91 100644 --- a/base/android/java/src/org/chromium/base/JNIUtils.java +++ b/base/android/java/src/org/chromium/base/JNIUtils.java
@@ -12,6 +12,8 @@ */ @MainDex public class JNIUtils { + private static Boolean sSelectiveJniRegistrationEnabled; + /** * This returns a ClassLoader that is capable of loading Chromium Java code. Such a ClassLoader * is needed for the few cases where the JNI mechanism is unable to automatically determine the @@ -21,4 +23,24 @@ public static Object getClassLoader() { return JNIUtils.class.getClassLoader(); } + + /** + * @return whether or not the current process supports selective JNI registration. + */ + @CalledByNative + public static boolean isSelectiveJniRegistrationEnabled() { + if (sSelectiveJniRegistrationEnabled == null) { + sSelectiveJniRegistrationEnabled = false; + } + return sSelectiveJniRegistrationEnabled; + } + + /** + * Allow this process to selectively perform JNI registration. This must be called before + * loading native libraries or it will have no effect. + */ + public static void enableSelectiveJniRegistration() { + assert sSelectiveJniRegistrationEnabled == null; + sSelectiveJniRegistrationEnabled = true; + } }
diff --git a/base/android/jni_android.cc b/base/android/jni_android.cc index 02e264c..4a88d339 100644 --- a/base/android/jni_android.cc +++ b/base/android/jni_android.cc
@@ -21,7 +21,8 @@ using base::android::MethodID; using base::android::ScopedJavaLocalRef; -bool g_disable_manual_jni_registration = false; +base::android::JniRegistrationType g_jni_registration_type = + base::android::ALL_JNI_REGISTRATION; JavaVM* g_jvm = NULL; base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky @@ -38,13 +39,12 @@ namespace base { namespace android { -bool IsManualJniRegistrationDisabled() { - return g_disable_manual_jni_registration; +JniRegistrationType GetJniRegistrationType() { + return g_jni_registration_type; } -void DisableManualJniRegistration() { - DCHECK(!g_disable_manual_jni_registration); - g_disable_manual_jni_registration = true; +void SetJniRegistrationType(JniRegistrationType jni_registration_type) { + g_jni_registration_type = jni_registration_type; } JNIEnv* AttachCurrentThread() {
diff --git a/base/android/jni_android.h b/base/android/jni_android.h index 9674653..de53c10f 100644 --- a/base/android/jni_android.h +++ b/base/android/jni_android.h
@@ -54,12 +54,23 @@ // Used to mark symbols to be exported in a shared library's symbol table. #define JNI_EXPORT __attribute__ ((visibility("default"))) -// Used to disable manual JNI registration in binaries that prefer to use native -// JNI exports for startup performance. This is not compatible with the crazy -// linker and so defaults to off. Call DisableManualJniRegistration at the very -// beginning of JNI_OnLoad to use this. -BASE_EXPORT bool IsManualJniRegistrationDisabled(); -BASE_EXPORT void DisableManualJniRegistration(); +// The level of JNI registration required for the current process. +enum JniRegistrationType { + // Register all native methods. + ALL_JNI_REGISTRATION, + // Register some native methods, as controlled by the jni_generator. + SELECTIVE_JNI_REGISTRATION, + // Do not register any native methods. + NO_JNI_REGISTRATION, +}; + +BASE_EXPORT JniRegistrationType GetJniRegistrationType(); + +// Set the JniRegistrationType for this process (defaults to +// ALL_JNI_REGISTRATION). This should be called in the JNI_OnLoad function +// which is called when the native library is first loaded. +BASE_EXPORT void SetJniRegistrationType( + JniRegistrationType jni_registration_type); // Contains the registration method information for initializing JNI bindings. struct RegistrationMethod {
diff --git a/base/android/jni_generator/SampleForTests_jni.golden b/base/android/jni_generator/SampleForTests_jni.golden index 6fdced2..7b384252 100644 --- a/base/android/jni_generator/SampleForTests_jni.golden +++ b/base/android/jni_generator/SampleForTests_jni.golden
@@ -483,7 +483,8 @@ }; static bool RegisterNativesImpl(JNIEnv* env) { - if (base::android::IsManualJniRegistrationDisabled()) return true; + if (jni_generator::ShouldSkipJniRegistration(false)) + return true; const int kMethodsInnerClassSize = arraysize(kMethodsInnerClass);
diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py index 0173a2c..99d8b424 100755 --- a/base/android/jni_generator/jni_generator.py +++ b/base/android/jni_generator/jni_generator.py
@@ -412,6 +412,19 @@ return natives +def IsMainDexJavaClass(contents): + """Returns "true" if the class is annotated with "@MainDex", "false" if not. + + JNI registration doesn't always need to be completed for non-browser processes + since most Java code is only used by the browser process. Classes that are + needed by non-browser processes must explicitly be annotated with @MainDex + to force JNI registration. + """ + re_maindex = re.compile(r'@MainDex[\s\S]*class\s+\w+\s*{') + found = re.search(re_maindex, contents) + return 'true' if found else 'false' + + def GetStaticCastForReturnType(return_type): type_map = { 'String' : 'jstring', 'java/lang/String' : 'jstring', @@ -634,8 +647,8 @@ value=value.group('value'))) self.inl_header_file_generator = InlHeaderFileGenerator( - self.namespace, self.fully_qualified_class, [], - self.called_by_natives, self.constant_fields, options) + self.namespace, self.fully_qualified_class, [], self.called_by_natives, + self.constant_fields, options) def GetContent(self): return self.inl_header_file_generator.GetContent() @@ -669,12 +682,13 @@ jni_namespace = ExtractJNINamespace(contents) or options.namespace natives = ExtractNatives(contents, options.ptr_type) called_by_natives = ExtractCalledByNatives(contents) + maindex = IsMainDexJavaClass(contents) if len(natives) == 0 and len(called_by_natives) == 0: raise SyntaxError('Unable to find any JNI methods for %s.' % fully_qualified_class) inl_header_file_generator = InlHeaderFileGenerator( - jni_namespace, fully_qualified_class, natives, called_by_natives, - [], options) + jni_namespace, fully_qualified_class, natives, called_by_natives, [], + options, maindex) self.content = inl_header_file_generator.GetContent() @classmethod @@ -710,7 +724,7 @@ """Generates an inline header file for JNI integration.""" def __init__(self, namespace, fully_qualified_class, natives, - called_by_natives, constant_fields, options): + called_by_natives, constant_fields, options, maindex='false'): self.namespace = namespace self.fully_qualified_class = fully_qualified_class self.class_name = self.fully_qualified_class.split('/')[-1] @@ -718,6 +732,7 @@ self.called_by_natives = called_by_natives self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI' self.constant_fields = constant_fields + self.maindex = maindex self.options = options @@ -862,8 +877,9 @@ early_exit = '' if self.options.native_exports_optional: early_exit = """\ - if (base::android::IsManualJniRegistrationDisabled()) return true; -""" + if (jni_generator::ShouldSkipJniRegistration(%s)) + return true; +""" % self.maindex values = {'REGISTER_NATIVES_SIGNATURE': signature, 'EARLY_EXIT': early_exit,
diff --git a/base/android/jni_generator/jni_generator_helper.h b/base/android/jni_generator/jni_generator_helper.h index 4c159c0..3062806 100644 --- a/base/android/jni_generator/jni_generator_helper.h +++ b/base/android/jni_generator/jni_generator_helper.h
@@ -42,6 +42,21 @@ base::android::CheckException(env); } +inline bool ShouldSkipJniRegistration(bool is_maindex_class) { + switch (base::android::GetJniRegistrationType()) { + case base::android::ALL_JNI_REGISTRATION: + return false; + case base::android::NO_JNI_REGISTRATION: + // TODO(estevenson): Change this to a DCHECK. + return true; + case base::android::SELECTIVE_JNI_REGISTRATION: + return !is_maindex_class; + default: + NOTREACHED(); + return false; + } +} + } // namespace jni_generator #endif // BASE_ANDROID_JNI_GENERATOR_JNI_GENERATOR_HELPER_H_
diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py index 022f043..c0c82388 100755 --- a/base/android/jni_generator/jni_generator_tests.py +++ b/base/android/jni_generator/jni_generator_tests.py
@@ -950,6 +950,33 @@ natives, [], [], test_options) self.assertGoldenTextEquals(h.GetContent()) + def testMainDexFile(self): + test_data = """ + package org.chromium.example.jni_generator; + + @MainDex + class Test { + private static native int nativeStaticMethod(long nativeTest, int arg1); + } + """ + options = TestOptions() + jni_from_java = jni_generator.JNIFromJavaSource( + test_data, 'org/chromium/foo/Bar', options) + self.assertGoldenTextEquals(jni_from_java.GetContent()) + + def testNonMainDexFile(self): + test_data = """ + package org.chromium.example.jni_generator; + + class Test { + private static native int nativeStaticMethod(long nativeTest, int arg1); + } + """ + options = TestOptions() + jni_from_java = jni_generator.JNIFromJavaSource( + test_data, 'org/chromium/foo/Bar', options) + self.assertGoldenTextEquals(jni_from_java.GetContent()) + def testNativeExportsOnlyOption(self): test_data = """ package org.chromium.example.jni_generator;
diff --git a/base/android/jni_generator/testInnerClassNatives.golden b/base/android/jni_generator/testInnerClassNatives.golden index 68ed943..20b8830 100644 --- a/base/android/jni_generator/testInnerClassNatives.golden +++ b/base/android/jni_generator/testInnerClassNatives.golden
@@ -52,7 +52,8 @@ }; static bool RegisterNativesImpl(JNIEnv* env) { - if (base::android::IsManualJniRegistrationDisabled()) return true; + if (jni_generator::ShouldSkipJniRegistration(false)) + return true; const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass);
diff --git a/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden b/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden index 0d7b93e..67352e7 100644 --- a/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden +++ b/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden
@@ -68,7 +68,8 @@ }; static bool RegisterNativesImpl(JNIEnv* env) { - if (base::android::IsManualJniRegistrationDisabled()) return true; + if (jni_generator::ShouldSkipJniRegistration(false)) + return true; const int kMethodsMyOtherInnerClassSize = arraysize(kMethodsMyOtherInnerClass);
diff --git a/base/android/jni_generator/testInnerClassNativesMultiple.golden b/base/android/jni_generator/testInnerClassNativesMultiple.golden index 8f37c68..7807efa 100644 --- a/base/android/jni_generator/testInnerClassNativesMultiple.golden +++ b/base/android/jni_generator/testInnerClassNativesMultiple.golden
@@ -75,7 +75,8 @@ }; static bool RegisterNativesImpl(JNIEnv* env) { - if (base::android::IsManualJniRegistrationDisabled()) return true; + if (jni_generator::ShouldSkipJniRegistration(false)) + return true; const int kMethodsMyOtherInnerClassSize = arraysize(kMethodsMyOtherInnerClass);
diff --git a/base/android/jni_generator/testMainDexFile.golden b/base/android/jni_generator/testMainDexFile.golden new file mode 100644 index 0000000..cbb2a7d --- /dev/null +++ b/base/android/jni_generator/testMainDexFile.golden
@@ -0,0 +1,67 @@ +// 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. + +// This file is autogenerated by +// base/android/jni_generator/jni_generator.py +// For +// org/chromium/foo/Bar + +#ifndef org_chromium_foo_Bar_JNI +#define org_chromium_foo_Bar_JNI + +#include <jni.h> + +#include "base/android/jni_generator/jni_generator_helper.h" + +#include "base/android/jni_int_wrapper.h" + +// Step 1: forward declarations. +namespace { +const char kBarClassPath[] = "org/chromium/foo/Bar"; +// Leaking this jclass as we cannot use LazyInstance from some threads. +base::subtle::AtomicWord g_Bar_clazz __attribute__((unused)) = 0; +#define Bar_clazz(env) base::android::LazyGetClass(env, kBarClassPath, &g_Bar_clazz) + +} // namespace + +// Step 2: method stubs. +JNI_GENERATOR_EXPORT jint Java_org_chromium_foo_Bar_nativeStaticMethod(JNIEnv* + env, jobject jcaller, + jlong nativeTest, + jint arg1) { + Test* native = reinterpret_cast<Test*>(nativeTest); + CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0); + return native->StaticMethod(env, base::android::JavaParamRef<jobject>(env, + jcaller), arg1); +} + +// Step 3: RegisterNatives. + +static const JNINativeMethod kMethodsBar[] = { + { "nativeStaticMethod", +"(" +"J" +"I" +")" +"I", reinterpret_cast<void*>(Java_org_chromium_foo_Bar_nativeStaticMethod) }, +}; + +static bool RegisterNativesImpl(JNIEnv* env) { + if (jni_generator::ShouldSkipJniRegistration(true)) + return true; + + const int kMethodsBarSize = arraysize(kMethodsBar); + + if (env->RegisterNatives(Bar_clazz(env), + kMethodsBar, + kMethodsBarSize) < 0) { + jni_generator::HandleRegistrationError( + env, Bar_clazz(env), __FILE__); + return false; + } + + return true; +} + +#endif // org_chromium_foo_Bar_JNI
diff --git a/base/android/jni_generator/testMultipleJNIAdditionalImport.golden b/base/android/jni_generator/testMultipleJNIAdditionalImport.golden index dfeebc11..0eecb5a 100644 --- a/base/android/jni_generator/testMultipleJNIAdditionalImport.golden +++ b/base/android/jni_generator/testMultipleJNIAdditionalImport.golden
@@ -76,7 +76,8 @@ }; static bool RegisterNativesImpl(JNIEnv* env) { - if (base::android::IsManualJniRegistrationDisabled()) return true; + if (jni_generator::ShouldSkipJniRegistration(false)) + return true; const int kMethodsFooSize = arraysize(kMethodsFoo);
diff --git a/base/android/jni_generator/testNatives.golden b/base/android/jni_generator/testNatives.golden index 2399cd7..3362c928 100644 --- a/base/android/jni_generator/testNatives.golden +++ b/base/android/jni_generator/testNatives.golden
@@ -321,7 +321,8 @@ }; static bool RegisterNativesImpl(JNIEnv* env) { - if (base::android::IsManualJniRegistrationDisabled()) return true; + if (jni_generator::ShouldSkipJniRegistration(false)) + return true; const int kMethodsTestJniSize = arraysize(kMethodsTestJni);
diff --git a/base/android/jni_generator/testNativesLong.golden b/base/android/jni_generator/testNativesLong.golden index 12a153e..ec029ce3 100644 --- a/base/android/jni_generator/testNativesLong.golden +++ b/base/android/jni_generator/testNativesLong.golden
@@ -47,7 +47,8 @@ }; static bool RegisterNativesImpl(JNIEnv* env) { - if (base::android::IsManualJniRegistrationDisabled()) return true; + if (jni_generator::ShouldSkipJniRegistration(false)) + return true; const int kMethodsTestJniSize = arraysize(kMethodsTestJni);
diff --git a/base/android/jni_generator/testNonMainDexFile.golden b/base/android/jni_generator/testNonMainDexFile.golden new file mode 100644 index 0000000..533241e --- /dev/null +++ b/base/android/jni_generator/testNonMainDexFile.golden
@@ -0,0 +1,67 @@ +// 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. + +// This file is autogenerated by +// base/android/jni_generator/jni_generator.py +// For +// org/chromium/foo/Bar + +#ifndef org_chromium_foo_Bar_JNI +#define org_chromium_foo_Bar_JNI + +#include <jni.h> + +#include "base/android/jni_generator/jni_generator_helper.h" + +#include "base/android/jni_int_wrapper.h" + +// Step 1: forward declarations. +namespace { +const char kBarClassPath[] = "org/chromium/foo/Bar"; +// Leaking this jclass as we cannot use LazyInstance from some threads. +base::subtle::AtomicWord g_Bar_clazz __attribute__((unused)) = 0; +#define Bar_clazz(env) base::android::LazyGetClass(env, kBarClassPath, &g_Bar_clazz) + +} // namespace + +// Step 2: method stubs. +JNI_GENERATOR_EXPORT jint Java_org_chromium_foo_Bar_nativeStaticMethod(JNIEnv* + env, jobject jcaller, + jlong nativeTest, + jint arg1) { + Test* native = reinterpret_cast<Test*>(nativeTest); + CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0); + return native->StaticMethod(env, base::android::JavaParamRef<jobject>(env, + jcaller), arg1); +} + +// Step 3: RegisterNatives. + +static const JNINativeMethod kMethodsBar[] = { + { "nativeStaticMethod", +"(" +"J" +"I" +")" +"I", reinterpret_cast<void*>(Java_org_chromium_foo_Bar_nativeStaticMethod) }, +}; + +static bool RegisterNativesImpl(JNIEnv* env) { + if (jni_generator::ShouldSkipJniRegistration(false)) + return true; + + const int kMethodsBarSize = arraysize(kMethodsBar); + + if (env->RegisterNatives(Bar_clazz(env), + kMethodsBar, + kMethodsBarSize) < 0) { + jni_generator::HandleRegistrationError( + env, Bar_clazz(env), __FILE__); + return false; + } + + return true; +} + +#endif // org_chromium_foo_Bar_JNI
diff --git a/base/android/jni_generator/testSingleJNIAdditionalImport.golden b/base/android/jni_generator/testSingleJNIAdditionalImport.golden index 1f5ac11..ef618da8 100644 --- a/base/android/jni_generator/testSingleJNIAdditionalImport.golden +++ b/base/android/jni_generator/testSingleJNIAdditionalImport.golden
@@ -70,7 +70,8 @@ }; static bool RegisterNativesImpl(JNIEnv* env) { - if (base::android::IsManualJniRegistrationDisabled()) return true; + if (jni_generator::ShouldSkipJniRegistration(false)) + return true; const int kMethodsFooSize = arraysize(kMethodsFoo);
diff --git a/base/android/jni_utils.cc b/base/android/jni_utils.cc index e11678df..848dfd74 100644 --- a/base/android/jni_utils.cc +++ b/base/android/jni_utils.cc
@@ -4,7 +4,6 @@ #include "base/android/jni_utils.h" -#include "base/android/jni_android.h" #include "base/android/scoped_java_ref.h" #include "jni/JNIUtils_jni.h" @@ -16,6 +15,10 @@ return Java_JNIUtils_getClassLoader(env); } +bool isSelectiveJniRegistrationEnabled(JNIEnv* env) { + return Java_JNIUtils_isSelectiveJniRegistrationEnabled(env); +} + } // namespace android } // namespace base
diff --git a/base/android/jni_utils.h b/base/android/jni_utils.h index 6e836ec..ef645c26 100644 --- a/base/android/jni_utils.h +++ b/base/android/jni_utils.h
@@ -18,6 +18,9 @@ // via JNI from Java. BASE_EXPORT ScopedJavaLocalRef<jobject> GetClassLoader(JNIEnv* env); +// Returns true if the current process permits selective JNI registration. +BASE_EXPORT bool isSelectiveJniRegistrationEnabled(JNIEnv* env); + } // namespace android } // namespace base
diff --git a/base/observer_list_threadsafe.h b/base/observer_list_threadsafe.h index afb1010..f8481de 100644 --- a/base/observer_list_threadsafe.h +++ b/base/observer_list_threadsafe.h
@@ -5,52 +5,48 @@ #ifndef BASE_OBSERVER_LIST_THREADSAFE_H_ #define BASE_OBSERVER_LIST_THREADSAFE_H_ -#include <algorithm> -#include <map> -#include <memory> -#include <tuple> +#include <unordered_map> #include "base/bind.h" #include "base/location.h" #include "base/logging.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" #include "base/observer_list.h" +#include "base/sequenced_task_runner.h" +#include "base/stl_util.h" +#include "base/synchronization/lock.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "base/threading/thread_local.h" +#include "build/build_config.h" + +// TODO(fdoray): Removing these includes causes IWYU failures in other headers, +// remove them in a follow- up CL. +#include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" -#include "base/threading/platform_thread.h" -#include "base/threading/thread_task_runner_handle.h" /////////////////////////////////////////////////////////////////////////////// // // OVERVIEW: // -// A thread-safe container for a list of observers. -// This is similar to the observer_list (see observer_list.h), but it -// is more robust for multi-threaded situations. +// A thread-safe container for a list of observers. This is similar to the +// observer_list (see observer_list.h), but it is more robust for multi- +// threaded situations. // // The following use cases are supported: -// * Observers can register for notifications from any thread. -// Callbacks to the observer will occur on the same thread where -// the observer initially called AddObserver() from. -// * Any thread may trigger a notification via Notify(). -// * Observers can remove themselves from the observer list inside -// of a callback. -// * If one thread is notifying observers concurrently with an observer -// removing itself from the observer list, the notifications will -// be silently dropped. +// * Observers can register for notifications from any sequence. They are +// always notified on the sequence from which they were registered. +// * Any sequence may trigger a notification via Notify(). +// * Observers can remove themselves from the observer list inside of a +// callback. +// * If one sequence is notifying observers concurrently with an observer +// removing itself from the observer list, the notifications will be +// silently dropped. // -// The drawback of the threadsafe observer list is that notifications -// are not as real-time as the non-threadsafe version of this class. -// Notifications will always be done via PostTask() to another thread, -// whereas with the non-thread-safe observer_list, notifications happen -// synchronously and immediately. -// -// IMPLEMENTATION NOTES -// The ObserverListThreadSafe maintains an ObserverList for each thread -// which uses the ThreadSafeObserver. When Notifying the observers, -// we simply call PostTask to each registered thread, and then each thread -// will notify its regular ObserverList. +// The drawback of the threadsafe observer list is that notifications are not +// as real-time as the non-threadsafe version of this class. Notifications +// will always be done via PostTask() to another sequence, whereas with the +// non-thread-safe observer_list, notifications happen synchronously. // /////////////////////////////////////////////////////////////////////////////// @@ -77,68 +73,72 @@ using NotificationType = typename ObserverList<ObserverType>::NotificationType; - ObserverListThreadSafe() - : type_(ObserverListBase<ObserverType>::NOTIFY_ALL) {} + ObserverListThreadSafe() = default; explicit ObserverListThreadSafe(NotificationType type) : type_(type) {} - // Add an observer to the list. An observer should not be added to - // the same list more than once. - void AddObserver(ObserverType* obs) { - // If there is no ThreadTaskRunnerHandle, it is impossible to notify on it, - // so do not add the observer. - if (!ThreadTaskRunnerHandle::IsSet()) + // Adds |observer| to the list. |observer| must not already be in the list. + void AddObserver(ObserverType* observer) { + // TODO(fdoray): Change this to a DCHECK once all call sites have a + // SequencedTaskRunnerHandle. + if (!SequencedTaskRunnerHandle::IsSet()) return; - ObserverList<ObserverType>* list = nullptr; - PlatformThreadId thread_id = PlatformThread::CurrentId(); - { - AutoLock lock(list_lock_); - if (observer_lists_.find(thread_id) == observer_lists_.end()) { - observer_lists_[thread_id] = - base::MakeUnique<ObserverListContext>(type_); + AutoLock auto_lock(lock_); + + // Add |observer| to the list of observers. + DCHECK(!ContainsKey(observers_, observer)); + const scoped_refptr<SequencedTaskRunner> task_runner = + SequencedTaskRunnerHandle::Get(); + observers_[observer] = task_runner; + + // If this is called while a notification is being dispatched on this thread + // and |type_| is NOTIFY_ALL, |observer| must be notified (if a notification + // is being dispatched on another thread in parallel, the notification may + // or may not make it to |observer| depending on the outcome of the race to + // |lock_|). + if (type_ == NotificationType::NOTIFY_ALL) { + const NotificationData* current_notification = + tls_current_notification_.Get(); + if (current_notification) { + task_runner->PostTask( + current_notification->from_here, + Bind(&ObserverListThreadSafe<ObserverType>::NotifyWrapper, this, + observer, *current_notification)); } - list = &(observer_lists_[thread_id]->list); } - list->AddObserver(obs); } // Remove an observer from the list if it is in the list. // If there are pending notifications in-transit to the observer, they will // be aborted. // If the observer to be removed is in the list, RemoveObserver MUST - // be called from the same thread which called AddObserver. - void RemoveObserver(ObserverType* obs) { - PlatformThreadId thread_id = PlatformThread::CurrentId(); - { - AutoLock lock(list_lock_); - auto it = observer_lists_.find(thread_id); - if (it == observer_lists_.end()) { - // This will happen if we try to remove an observer on a thread - // we never added an observer for. - return; - } - ObserverList<ObserverType>& list = it->second->list; + // be called from the same sequence which called AddObserver. + void RemoveObserver(ObserverType* observer) { + AutoLock auto_lock(lock_); + auto it = observers_.find(observer); + if (it == observers_.end()) + return; - list.RemoveObserver(obs); + // TODO(fdoray): Enable this on Android once all tests pass. +#if !defined(OS_ANDROID) + DCHECK(it->second->RunsTasksOnCurrentThread()); +#endif - // If that was the last observer in the list, remove the ObserverList - // entirely. - if (list.size() == 0) - observer_lists_.erase(it); - } + observers_.erase(it); } // Verifies that the list is currently empty (i.e. there are no observers). void AssertEmpty() const { - AutoLock lock(list_lock_); - DCHECK(observer_lists_.empty()); +#if DCHECK_IS_ON() + AutoLock auto_lock(lock_); + DCHECK(observers_.empty()); +#endif } - // Notify methods. - // Make a thread-safe callback to each Observer in the list. - // Note, these calls are effectively asynchronous. You cannot assume - // that at the completion of the Notify call that all Observers have - // been Notified. The notification may still be pending delivery. + // Asynchronously invokes a callback on all observers, on their registration + // sequence. You cannot assume that at the completion of the Notify call that + // all Observers have been Notified. The notification may still be pending + // delivery. template <typename Method, typename... Params> void Notify(const tracked_objects::Location& from_here, Method m, Params&&... params) { @@ -146,79 +146,71 @@ Bind(&internal::Dispatcher<ObserverType, Method>::Run, m, std::forward<Params>(params)...); - AutoLock lock(list_lock_); - for (const auto& entry : observer_lists_) { - ObserverListContext* context = entry.second.get(); - context->task_runner->PostTask( + AutoLock lock(lock_); + for (const auto& observer : observers_) { + observer.second->PostTask( from_here, - Bind(&ObserverListThreadSafe<ObserverType>::NotifyWrapper, - this, context, method)); + Bind(&ObserverListThreadSafe<ObserverType>::NotifyWrapper, this, + observer.first, NotificationData(from_here, method))); } } private: friend class RefCountedThreadSafe<ObserverListThreadSafe<ObserverType>>; - struct ObserverListContext { - explicit ObserverListContext(NotificationType type) - : task_runner(ThreadTaskRunnerHandle::Get()), list(type) {} + struct NotificationData { + NotificationData(const tracked_objects::Location& from_here_in, + const Callback<void(ObserverType*)>& method_in) + : from_here(from_here_in), method(method_in) {} - scoped_refptr<SingleThreadTaskRunner> task_runner; - ObserverList<ObserverType> list; - - private: - DISALLOW_COPY_AND_ASSIGN(ObserverListContext); + tracked_objects::Location from_here; + Callback<void(ObserverType*)> method; }; - ~ObserverListThreadSafe() { - } + ~ObserverListThreadSafe() = default; - // Wrapper which is called to fire the notifications for each thread's - // ObserverList. This function MUST be called on the thread which owns - // the unsafe ObserverList. - void NotifyWrapper(ObserverListContext* context, - const Callback<void(ObserverType*)>& method) { - // Check that this list still needs notifications. + void NotifyWrapper(ObserverType* observer, + const NotificationData& notification) { { - AutoLock lock(list_lock_); - auto it = observer_lists_.find(PlatformThread::CurrentId()); + AutoLock auto_lock(lock_); - // The ObserverList could have been removed already. In fact, it could - // have been removed and then re-added! If the master list's loop - // does not match this one, then we do not need to finish this - // notification. - if (it == observer_lists_.end() || it->second.get() != context) + // Check whether the observer still needs a notification. + auto it = observers_.find(observer); + if (it == observers_.end()) return; + DCHECK(it->second->RunsTasksOnCurrentThread()); } - for (auto& observer : context->list) { - method.Run(&observer); - } + // Keep track of the notification being dispatched on the current thread. + // This will be used if the callback below calls AddObserver(). + // + // Note: |tls_current_notification_| may not be nullptr if this runs in a + // nested loop started by a notification callback. In that case, it is + // important to save the previous value to restore it later. + const NotificationData* const previous_notification = + tls_current_notification_.Get(); + tls_current_notification_.Set(¬ification); - // If there are no more observers on the list, we can now delete it. - if (context->list.size() == 0) { - { - AutoLock lock(list_lock_); - // Remove |list| if it's not already removed. - // This can happen if multiple observers got removed in a notification. - // See http://crbug.com/55725. - auto it = observer_lists_.find(PlatformThread::CurrentId()); - if (it != observer_lists_.end() && it->second.get() == context) - observer_lists_.erase(it); - } - } + // Invoke the callback. + notification.method.Run(observer); + + // Reset the notification being dispatched on the current thread to its + // previous value. + tls_current_notification_.Set(previous_notification); } - mutable Lock list_lock_; // Protects the observer_lists_. + const NotificationType type_ = NotificationType::NOTIFY_ALL; - // Key by PlatformThreadId because in tests, clients can attempt to remove - // observers without a SingleThreadTaskRunner. If this were keyed by - // SingleThreadTaskRunner, that operation would be silently ignored, leaving - // garbage in the ObserverList. - std::map<PlatformThreadId, std::unique_ptr<ObserverListContext>> - observer_lists_; + // Synchronizes access to |observers_|. + mutable Lock lock_; - const NotificationType type_; + // Keys are observers. Values are the SequencedTaskRunners on which they must + // be notified. + std::unordered_map<ObserverType*, scoped_refptr<SequencedTaskRunner>> + observers_; + + // Notification being dispatched on the current thread. + ThreadLocalPointer<const NotificationData> tls_current_notification_; DISALLOW_COPY_AND_ASSIGN(ObserverListThreadSafe); };
diff --git a/base/observer_list_unittest.cc b/base/observer_list_unittest.cc index c5e556b..653d881 100644 --- a/base/observer_list_unittest.cc +++ b/base/observer_list_unittest.cc
@@ -5,13 +5,18 @@ #include "base/observer_list.h" #include "base/observer_list_threadsafe.h" +#include <utility> #include <vector> +#include "base/bind.h" #include "base/compiler_specific.h" #include "base/location.h" #include "base/memory/weak_ptr.h" #include "base/run_loop.h" +#include "base/sequenced_task_runner.h" #include "base/single_thread_task_runner.h" +#include "base/task_scheduler/post_task.h" +#include "base/test/scoped_task_scheduler.h" #include "base/threading/platform_thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -65,20 +70,6 @@ bool remove_self_; }; -class ThreadSafeDisrupter : public Foo { - public: - ThreadSafeDisrupter(ObserverListThreadSafe<Foo>* list, Foo* doomed) - : list_(list), - doomed_(doomed) { - } - ~ThreadSafeDisrupter() override {} - void Observe(int x) override { list_->RemoveObserver(doomed_); } - - private: - ObserverListThreadSafe<Foo>* list_; - Foo* doomed_; -}; - template <typename ObserverListType> class AddInObserve : public Foo { public: @@ -276,7 +267,6 @@ Adder b(-1); Adder c(1); Adder d(-1); - ThreadSafeDisrupter evil(observer_list.get(), &c); observer_list->AddObserver(&a); observer_list->AddObserver(&b); @@ -284,11 +274,11 @@ observer_list->Notify(FROM_HERE, &Foo::Observe, 10); RunLoop().RunUntilIdle(); - observer_list->AddObserver(&evil); observer_list->AddObserver(&c); observer_list->AddObserver(&d); observer_list->Notify(FROM_HERE, &Foo::Observe, 10); + observer_list->RemoveObserver(&c); RunLoop().RunUntilIdle(); EXPECT_EQ(20, a.total); @@ -329,18 +319,18 @@ EXPECT_EQ(0, b.total); } -TEST(ObserverListThreadSafeTest, WithoutMessageLoop) { +TEST(ObserverListThreadSafeTest, WithoutSequence) { scoped_refptr<ObserverListThreadSafe<Foo> > observer_list( new ObserverListThreadSafe<Foo>); Adder a(1), b(1), c(1); - // No MessageLoop, so these should not be added. + // No sequence, so these should not be added. observer_list->AddObserver(&a); observer_list->AddObserver(&b); { - // Add c when there's a loop. + // Add c when there's a sequence. MessageLoop loop; observer_list->AddObserver(&c); @@ -351,10 +341,10 @@ EXPECT_EQ(0, b.total); EXPECT_EQ(10, c.total); - // Now add a when there's a loop. + // Now add a when there's a sequence. observer_list->AddObserver(&a); - // Remove c when there's a loop. + // Remove c when there's a sequence. observer_list->RemoveObserver(&c); // Notify again. @@ -366,7 +356,7 @@ EXPECT_EQ(10, c.total); } - // Removing should always succeed with or without a loop. + // Removing should always succeed with or without a sequence. observer_list->RemoveObserver(&a); // Notifying should not fail but should also be a no-op. @@ -491,6 +481,79 @@ observer_list->Notify(FROM_HERE, &Foo::Observe, 1); } +namespace { + +class SequenceVerificationObserver : public Foo { + public: + explicit SequenceVerificationObserver( + scoped_refptr<SequencedTaskRunner> task_runner) + : task_runner_(std::move(task_runner)) {} + ~SequenceVerificationObserver() override = default; + + void Observe(int x) override { + called_on_valid_sequence_ = task_runner_->RunsTasksOnCurrentThread(); + } + + bool called_on_valid_sequence() const { return called_on_valid_sequence_; } + + private: + const scoped_refptr<SequencedTaskRunner> task_runner_; + bool called_on_valid_sequence_ = false; + + DISALLOW_COPY_AND_ASSIGN(SequenceVerificationObserver); +}; + +} // namespace + +// Verify that observers are notified on the correct sequence. +TEST(ObserverListThreadSafeTest, NotificationOnValidSequence) { + test::ScopedTaskScheduler scoped_task_scheduler; + + auto task_runner_1 = CreateSequencedTaskRunnerWithTraits(TaskTraits()); + auto task_runner_2 = CreateSequencedTaskRunnerWithTraits(TaskTraits()); + + auto observer_list = make_scoped_refptr(new ObserverListThreadSafe<Foo>()); + + SequenceVerificationObserver observer_1(task_runner_1); + SequenceVerificationObserver observer_2(task_runner_2); + + task_runner_1->PostTask( + FROM_HERE, Bind(&ObserverListThreadSafe<Foo>::AddObserver, observer_list, + Unretained(&observer_1))); + task_runner_2->PostTask( + FROM_HERE, Bind(&ObserverListThreadSafe<Foo>::AddObserver, observer_list, + Unretained(&observer_2))); + + RunLoop().RunUntilIdle(); + + observer_list->Notify(FROM_HERE, &Foo::Observe, 1); + + RunLoop().RunUntilIdle(); + + EXPECT_TRUE(observer_1.called_on_valid_sequence()); + EXPECT_TRUE(observer_2.called_on_valid_sequence()); +} + +// Verify that when an observer is added to a NOTIFY_ALL ObserverListThreadSafe +// from a notification, it is itself notified. +TEST(ObserverListThreadSafeTest, AddObserverFromNotificationNotifyAll) { + MessageLoop message_loop; + auto observer_list = make_scoped_refptr(new ObserverListThreadSafe<Foo>()); + + Adder observer_added_from_notification(1); + + AddInObserve<ObserverListThreadSafe<Foo>> initial_observer( + observer_list.get()); + initial_observer.SetToAdd(&observer_added_from_notification); + observer_list->AddObserver(&initial_observer); + + observer_list->Notify(FROM_HERE, &Foo::Observe, 1); + + RunLoop().RunUntilIdle(); + + EXPECT_EQ(1, observer_added_from_notification.GetValue()); +} + TEST(ObserverListTest, Existing) { ObserverList<Foo> observer_list(ObserverList<Foo>::NOTIFY_EXISTING_ONLY); Adder a(1);
diff --git a/base/task_scheduler/scheduler_worker_pool_impl.cc b/base/task_scheduler/scheduler_worker_pool_impl.cc index 93800eb3..66fe929 100644 --- a/base/task_scheduler/scheduler_worker_pool_impl.cc +++ b/base/task_scheduler/scheduler_worker_pool_impl.cc
@@ -403,6 +403,10 @@ histograms->push_back(num_tasks_between_waits_histogram_); } +int SchedulerWorkerPoolImpl::GetMaxConcurrentTasksDeprecated() const { + return workers_.size(); +} + void SchedulerWorkerPoolImpl::WaitForAllWorkersIdleForTesting() { AutoSchedulerLock auto_lock(idle_workers_stack_lock_); while (idle_workers_stack_.Size() < workers_.size())
diff --git a/base/task_scheduler/scheduler_worker_pool_impl.h b/base/task_scheduler/scheduler_worker_pool_impl.h index 3205dae7..253f2020 100644 --- a/base/task_scheduler/scheduler_worker_pool_impl.h +++ b/base/task_scheduler/scheduler_worker_pool_impl.h
@@ -88,6 +88,11 @@ void GetHistograms(std::vector<const HistogramBase*>* histograms) const; + // Returns the maximum number of tasks that can run concurrently in this pool. + // + // TODO(fdoray): Remove this method. https://crbug.com/687264 + int GetMaxConcurrentTasksDeprecated() const; + // Waits until all workers are idle. void WaitForAllWorkersIdleForTesting();
diff --git a/base/task_scheduler/task_scheduler.h b/base/task_scheduler/task_scheduler.h index a73929c..533b7dfd 100644 --- a/base/task_scheduler/task_scheduler.h +++ b/base/task_scheduler/task_scheduler.h
@@ -17,6 +17,10 @@ #include "base/task_scheduler/task_traits.h" #include "base/time/time.h" +namespace gin { +class V8Platform; +} + namespace tracked_objects { class Location; } @@ -122,6 +126,20 @@ // SetInstance(). This should be used very rarely; most users of TaskScheduler // should use the post_task.h API. static TaskScheduler* GetInstance(); + + private: + friend class gin::V8Platform; + + // Returns the maximum number of non-single-threaded tasks posted with + // |traits| that can run concurrently in this TaskScheduler. + // + // Do not use this method. To process n items, post n tasks that each process + // 1 item rather than GetMaxConcurrentTasksWithTraitsDeprecated() tasks that + // each process n/GetMaxConcurrentTasksWithTraitsDeprecated() items. + // + // TODO(fdoray): Remove this method. https://crbug.com/687264 + virtual int GetMaxConcurrentTasksWithTraitsDeprecated( + const TaskTraits& traits) const = 0; }; } // namespace base
diff --git a/base/task_scheduler/task_scheduler_impl.cc b/base/task_scheduler/task_scheduler_impl.cc index f727c8c3..6157635 100644 --- a/base/task_scheduler/task_scheduler_impl.cc +++ b/base/task_scheduler/task_scheduler_impl.cc
@@ -78,6 +78,11 @@ return histograms; } +int TaskSchedulerImpl::GetMaxConcurrentTasksWithTraitsDeprecated( + const TaskTraits& traits) const { + return GetWorkerPoolForTraits(traits)->GetMaxConcurrentTasksDeprecated(); +} + void TaskSchedulerImpl::Shutdown() { // TODO(fdoray): Increase the priority of BACKGROUND tasks blocking shutdown. DCHECK(task_tracker_); @@ -161,8 +166,8 @@ } } -SchedulerWorkerPool* TaskSchedulerImpl::GetWorkerPoolForTraits( - const TaskTraits& traits) { +SchedulerWorkerPoolImpl* TaskSchedulerImpl::GetWorkerPoolForTraits( + const TaskTraits& traits) const { const size_t index = worker_pool_index_for_traits_callback_.Run(traits); DCHECK_LT(index, worker_pools_.size()); return worker_pools_[index].get();
diff --git a/base/task_scheduler/task_scheduler_impl.h b/base/task_scheduler/task_scheduler_impl.h index 7a083c6..1e80d5c5 100644 --- a/base/task_scheduler/task_scheduler_impl.h +++ b/base/task_scheduler/task_scheduler_impl.h
@@ -5,8 +5,6 @@ #ifndef BASE_TASK_SCHEDULER_TASK_SCHEDULER_IMPL_H_ #define BASE_TASK_SCHEDULER_TASK_SCHEDULER_IMPL_H_ -#include <stddef.h> - #include <memory> #include <vector> @@ -58,6 +56,8 @@ scoped_refptr<SingleThreadTaskRunner> CreateSingleThreadTaskRunnerWithTraits( const TaskTraits& traits) override; std::vector<const HistogramBase*> GetHistograms() const override; + int GetMaxConcurrentTasksWithTraitsDeprecated( + const TaskTraits& traits) const override; void Shutdown() override; void FlushForTesting() override; void JoinForTesting() override; @@ -70,7 +70,8 @@ const std::vector<SchedulerWorkerPoolParams>& worker_pool_params_vector); // Returns the worker pool that runs Tasks with |traits|. - SchedulerWorkerPool* GetWorkerPoolForTraits(const TaskTraits& traits); + SchedulerWorkerPoolImpl* GetWorkerPoolForTraits( + const TaskTraits& traits) const; // Callback invoked when a non-single-thread |sequence| isn't empty after a // worker pops a Task from it.
diff --git a/base/task_scheduler/task_scheduler_impl_unittest.cc b/base/task_scheduler/task_scheduler_impl_unittest.cc index cda0606..6aaf4af 100644 --- a/base/task_scheduler/task_scheduler_impl_unittest.cc +++ b/base/task_scheduler/task_scheduler_impl_unittest.cc
@@ -302,8 +302,25 @@ } } -// TODO(fdoray): Add tests with Sequences that move around worker pools once -// child TaskRunners are supported. +TEST_F(TaskSchedulerImplTest, GetMaxConcurrentTasksWithTraitsDeprecated) { + EXPECT_EQ(1, scheduler_->GetMaxConcurrentTasksWithTraitsDeprecated( + TaskTraits().WithPriority(TaskPriority::BACKGROUND))); + EXPECT_EQ( + 3, scheduler_->GetMaxConcurrentTasksWithTraitsDeprecated( + TaskTraits().WithPriority(TaskPriority::BACKGROUND).MayBlock())); + EXPECT_EQ(4, scheduler_->GetMaxConcurrentTasksWithTraitsDeprecated( + TaskTraits().WithPriority(TaskPriority::USER_VISIBLE))); + EXPECT_EQ( + 12, + scheduler_->GetMaxConcurrentTasksWithTraitsDeprecated( + TaskTraits().WithPriority(TaskPriority::USER_VISIBLE).MayBlock())); + EXPECT_EQ(4, scheduler_->GetMaxConcurrentTasksWithTraitsDeprecated( + TaskTraits().WithPriority(TaskPriority::USER_BLOCKING))); + EXPECT_EQ( + 12, + scheduler_->GetMaxConcurrentTasksWithTraitsDeprecated( + TaskTraits().WithPriority(TaskPriority::USER_BLOCKING).MayBlock())); +} } // namespace internal } // namespace base
diff --git a/base/test/scoped_task_scheduler.cc b/base/test/scoped_task_scheduler.cc index f068a199..3482f06 100644 --- a/base/test/scoped_task_scheduler.cc +++ b/base/test/scoped_task_scheduler.cc
@@ -51,6 +51,8 @@ scoped_refptr<SingleThreadTaskRunner> CreateSingleThreadTaskRunnerWithTraits( const TaskTraits& traits) override; std::vector<const HistogramBase*> GetHistograms() const override; + int GetMaxConcurrentTasksWithTraitsDeprecated( + const TaskTraits& traits) const override; void Shutdown() override; void FlushForTesting() override; void JoinForTesting() override; @@ -156,6 +158,11 @@ return std::vector<const HistogramBase*>(); } +int TestTaskScheduler::GetMaxConcurrentTasksWithTraitsDeprecated( + const TaskTraits& traits) const { + return 1; +} + void TestTaskScheduler::Shutdown() { // Prevent SKIP_ON_SHUTDOWN and CONTINUE_ON_SHUTDOWN tasks from running from // now on. @@ -247,10 +254,9 @@ ScopedTaskScheduler::ScopedTaskScheduler() : ScopedTaskScheduler(nullptr) {} -ScopedTaskScheduler::ScopedTaskScheduler(MessageLoop* external_message_loop) { +ScopedTaskScheduler::ScopedTaskScheduler(MessageLoop* message_loop) { DCHECK(!TaskScheduler::GetInstance()); - TaskScheduler::SetInstance( - MakeUnique<TestTaskScheduler>(external_message_loop)); + TaskScheduler::SetInstance(MakeUnique<TestTaskScheduler>(message_loop)); task_scheduler_ = TaskScheduler::GetInstance(); }
diff --git a/base/test/scoped_task_scheduler.h b/base/test/scoped_task_scheduler.h index bcb98c0..a371647e 100644 --- a/base/test/scoped_task_scheduler.h +++ b/base/test/scoped_task_scheduler.h
@@ -44,13 +44,41 @@ // runs "D" because it's BLOCK_SHUTDOWN. "C" is skipped. class ScopedTaskScheduler { public: - // Registers a TaskScheduler that instantiates a MessageLoop on the current - // thread and runs its tasks on it. + // Registers a synchronous TaskScheduler on a thread that doesn't have a + // MessageLoop. + // + // This constructor handles most common cases. ScopedTaskScheduler(); - // Registers a TaskScheduler that runs its tasks on |external_message_loop|. - // |external_message_loop| must be bound to the current thread. - explicit ScopedTaskScheduler(MessageLoop* external_message_loop); + // Registers a synchronous TaskScheduler on a thread that already has a + // |message_loop|. Calling RunLoop::Run/RunUntilIdle() on the thread where + // this lives runs the MessageLoop and TaskScheduler tasks in posting order. + // + // In general, you don't need a ScopedTaskScheduler and a MessageLoop because + // ScopedTaskScheduler provides most MessageLoop features. + // + // ScopedTaskScheduler scoped_task_scheduler; + // ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&Task)); + // RunLoop().RunUntilIdle(); // Runs Task. + // + // is equivalent to + // + // MessageLoop message_loop; + // ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&Task)); + // RunLoop().RunUntilIdle(); // Runs Task. + // + // Use this constructor if you need a non-default MessageLoop (e.g. + // MessageLoopFor(UI|IO)). + // + // MessageLoopForIO message_loop_for_io; + // ScopedTaskScheduler scoped_task_scheduler(&message_loop_for_io); + // message_loop_for_io->WatchFileDescriptor(...); + // message_loop_for_io->task_runner()->PostTask( + // FROM_HERE, &MessageLoopTask); + // PostTaskWithTraits(FROM_HERE, TaskTraits(), Bind(&TaskSchedulerTask)); + // RunLoop().RunUntilIdle(); // Runs both MessageLoopTask and + // // TaskSchedulerTask. + explicit ScopedTaskScheduler(MessageLoop* message_loop); // Runs all pending BLOCK_SHUTDOWN tasks and unregisters the TaskScheduler. ~ScopedTaskScheduler();
diff --git a/build/android/gradle/generate_gradle.py b/build/android/gradle/generate_gradle.py index 2686500..fbe0966 100755 --- a/build/android/gradle/generate_gradle.py +++ b/build/android/gradle/generate_gradle.py
@@ -488,7 +488,6 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument('--output-directory', - default='out/Debug', help='Path to the root build directory.') parser.add_argument('-v', '--verbose',
diff --git a/build/toolchain/gcc_toolchain.gni b/build/toolchain/gcc_toolchain.gni index 6102c8dd..b319806 100644 --- a/build/toolchain/gcc_toolchain.gni +++ b/build/toolchain/gcc_toolchain.gni
@@ -489,6 +489,10 @@ ld = cxx if (use_clang_static_analyzer) { + # Static analysis isn't supported under GOMA. See crbug.com/687245 + # for progress on this issue. + assert(!use_goma, "'use_clang_static_analyzer' cannot be used with GOMA.") + # Call "ccc-analyzer" or "c++-analyzer" instead of directly calling Clang. # |wrapper_tool| sets the environment variables which are read by the # analyzer tools.
diff --git a/build/toolchain/mac/BUILD.gn b/build/toolchain/mac/BUILD.gn index 8de58ce..de63e5d5 100644 --- a/build/toolchain/mac/BUILD.gn +++ b/build/toolchain/mac/BUILD.gn
@@ -124,7 +124,11 @@ cc = compiler_prefix + _cc cxx = compiler_prefix + _cxx - if (use_clang_static_analyzer && !toolchain_uses_goma) { + if (use_clang_static_analyzer) { + # Static analysis isn't supported under GOMA. See crbug.com/687245 + # for progress on this issue. + assert(!use_goma, "'use_clang_static_analyzer' cannot be used with GOMA.") + # Call "ccc-analyzer" or "c++-analyzer" instead of directly calling Clang. # |wrapper_tool| sets the environment variables which are read by the # analyzer tools.
diff --git a/cc/ipc/BUILD.gn b/cc/ipc/BUILD.gn index 29084cc..53b303a 100644 --- a/cc/ipc/BUILD.gn +++ b/cc/ipc/BUILD.gn
@@ -42,6 +42,7 @@ "begin_frame_args.mojom", "compositor_frame.mojom", "compositor_frame_metadata.mojom", + "copy_output_request.mojom", "filter_operation.mojom", "filter_operations.mojom", "frame_sink_id.mojom",
diff --git a/cc/ipc/cc_serialization_perftest.cc b/cc/ipc/cc_serialization_perftest.cc index c92c336..a63324a 100644 --- a/cc/ipc/cc_serialization_perftest.cc +++ b/cc/ipc/cc_serialization_perftest.cc
@@ -44,8 +44,10 @@ return IPC::ParamTraits<CompositorFrame>::Read(msg, &iter, frame); } - static void RunDeserializationTestParamTraits(const std::string& test_name, - const CompositorFrame& frame) { + static void RunDeserializationTestParamTraits( + const std::string& test_name, + const CompositorFrame& frame, + UseSingleSharedQuadState single_sqs) { IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); IPC::ParamTraits<CompositorFrame>::Write(&msg, frame); for (int i = 0; i < kNumWarmupRuns; ++i) { @@ -75,15 +77,23 @@ } perf_test::PrintResult( - "ParamTraits deserialization: min_frame_deserialization_time", "", + "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", - "", test_name, count, "", true); + single_sqs == UseSingleSharedQuadState::YES + ? "_per_render_pass_shared_quad_state" + : "_per_quad_shared_quad_state", + test_name, count, "", true); } - static void RunSerializationTestParamTraits(const std::string& test_name, - const CompositorFrame& frame) { + static void RunSerializationTestParamTraits( + const std::string& test_name, + const CompositorFrame& frame, + UseSingleSharedQuadState single_sqs) { for (int i = 0; i < kNumWarmupRuns; ++i) { IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); IPC::ParamTraits<CompositorFrame>::Write(&msg, frame); @@ -111,15 +121,23 @@ } perf_test::PrintResult( - "ParamTraits serialization: min_frame_serialization_time", "", + "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", - "", test_name, count, "", true); + single_sqs == UseSingleSharedQuadState::YES + ? "_per_render_pass_shared_quad_state" + : "_per_quad_shared_quad_state", + test_name, count, "", true); } - static void RunDeserializationTestStructTraits(const std::string& test_name, - const CompositorFrame& frame) { + static void RunDeserializationTestStructTraits( + const std::string& test_name, + const CompositorFrame& frame, + UseSingleSharedQuadState single_sqs) { auto data = mojom::CompositorFrame::Serialize(&frame); DCHECK_GT(data.size(), 0u); for (int i = 0; i < kNumWarmupRuns; ++i) { @@ -149,16 +167,24 @@ } perf_test::PrintResult( - "StructTraits deserialization min_frame_deserialization_time", "", + "StructTraits 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( - "StructTraits deserialization: num runs in 2 seconds", "", test_name, - count, "", true); + "StructTraits deserialization: num runs in 2 seconds", + single_sqs == UseSingleSharedQuadState::YES + ? "_per_render_pass_shared_quad_state" + : "_per_quad_shared_quad_state", + test_name, count, "", true); } - static void RunSerializationTestStructTraits(const std::string& test_name, - const CompositorFrame& frame) { + static void RunSerializationTestStructTraits( + const std::string& test_name, + const CompositorFrame& frame, + UseSingleSharedQuadState single_sqs) { for (int i = 0; i < kNumWarmupRuns; ++i) { auto data = mojom::CompositorFrame::Serialize(&frame); DCHECK_GT(data.size(), 0u); @@ -186,11 +212,17 @@ } perf_test::PrintResult( - "StructTraits serialization min_frame_serialization_time", "", + "StructTraits 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("StructTraits serialization: num runs in 2 seconds", - "", test_name, count, "", true); + single_sqs == UseSingleSharedQuadState::YES + ? "_per_render_pass_shared_quad_state" + : "_per_quad_shared_quad_state", + test_name, count, "", true); } static void RunCompositorFrameTest(const std::string& test_name, @@ -214,49 +246,80 @@ } frame.render_pass_list.push_back(std::move(render_pass)); } - RunTest(test_name, std::move(frame)); + RunTest(test_name, std::move(frame), single_sqs); } - static void RunTest(const std::string& test_name, CompositorFrame frame) { - RunSerializationTestStructTraits(test_name, frame); - RunDeserializationTestStructTraits(test_name, frame); - RunSerializationTestParamTraits(test_name, frame); - RunDeserializationTestParamTraits(test_name, frame); + static void RunTest(const std::string& test_name, + 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); } }; +// Test for compositor frames with one render pass and 4000 quads. TEST_F(CCSerializationPerfTest, 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); + // Case 2: One shared quad state for each quad. + RunCompositorFrameTest("DelegatedFrame_ManyQuads_1_4000", 4000, 1, + UseSingleSharedQuadState::NO); } +// Test for compositor frames with one render pass and 100000 quads. TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyQuads_1_100000) { + // Case 1: One shared quad state for all quads in one render pass. RunCompositorFrameTest("DelegatedFrame_ManyQuads_1_100000", 100000, 1, UseSingleSharedQuadState::YES); + // Case 2: One shared quad state for each quad. + RunCompositorFrameTest("DelegatedFrame_ManyQuads_1_100000", 100000, 1, + UseSingleSharedQuadState::NO); +} + +// Test for compositor frames with 100 render pass and each with 4000 quads. +TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyQuads_100_4000) { + // One shared quad state for all quads in one render pass. + RunCompositorFrameTest("DelegatedFrame_ManyQuads_100_4000", 4000, 100, + UseSingleSharedQuadState::YES); } -TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyQuads_4000_4000) { - RunCompositorFrameTest("DelegatedFrame_ManyQuads_4000_4000", 4000, 1, - UseSingleSharedQuadState::NO); +// Test for compositor frames with 10 render pass and each with 100000 quads. +TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyQuads_10_100000) { + // One shared quad state for all quads in one render pass. + RunCompositorFrameTest("DelegatedFrame_ManyQuads_10_100000", 100000, 10, + UseSingleSharedQuadState::YES); } -TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyQuads_100000_100000) { - RunCompositorFrameTest("DelegatedFrame_ManyQuads_100000_100000", 100000, 1, - UseSingleSharedQuadState::NO); -} - +// Test for compositor frames with 5 render pass and each with 100 quads. TEST_F(CCSerializationPerfTest, 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); + // Case 2: One shared quad state for each quad. RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_5_100", 100, 5, UseSingleSharedQuadState::NO); } +// Test for compositor frames with 10 render pass and each with 500 quads. TEST_F(CCSerializationPerfTest, 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); + // Case 2: One shared quad state for each quad. RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_10_500", 500, 10, UseSingleSharedQuadState::NO); } +// Test for compositor frames with 1000 render pass and each with 100 quads. TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyRenderPasses_1000_100) { - RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_10000_100", 100, 1000, + // Case 1: One shared quad state for all quads in one render pass. + RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_1000_100", 100, 1000, + UseSingleSharedQuadState::YES); + // Case 2: One shared quad state for each quad. + RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_1000_100", 100, 1000, UseSingleSharedQuadState::NO); }
diff --git a/cc/ipc/copy_output_request.mojom b/cc/ipc/copy_output_request.mojom new file mode 100644 index 0000000..c6ff8bec --- /dev/null +++ b/cc/ipc/copy_output_request.mojom
@@ -0,0 +1,18 @@ +// 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. + +module cc.mojom; + +import "cc/ipc/texture_mailbox.mojom"; +import "mojo/common/unguessable_token.mojom"; +import "ui/gfx/geometry/mojo/geometry.mojom"; + +// See cc/output/copy_output_request.h. +// Note: result_callback_ is not included in this struct. +struct CopyOutputRequest { + mojo.common.mojom.UnguessableToken? source; + bool force_bitmap_result; + gfx.mojom.Rect? area; + cc.mojom.TextureMailbox? texture_mailbox; +};
diff --git a/cc/ipc/copy_output_request.typemap b/cc/ipc/copy_output_request.typemap new file mode 100644 index 0000000..344c112 --- /dev/null +++ b/cc/ipc/copy_output_request.typemap
@@ -0,0 +1,11 @@ +# 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. + +mojom = "//cc/ipc/copy_output_request.mojom" +public_headers = [ "//cc/output/copy_output_request.h" ] +traits_headers = [ "//cc/ipc/copy_output_request_struct_traits.h" ] +sources = [ + "//cc/ipc:struct_traits", +] +type_mappings = [ "cc.mojom.CopyOutputRequest=cc::CopyOutputRequest" ]
diff --git a/cc/ipc/copy_output_request_struct_traits.h b/cc/ipc/copy_output_request_struct_traits.h new file mode 100644 index 0000000..c05c55c --- /dev/null +++ b/cc/ipc/copy_output_request_struct_traits.h
@@ -0,0 +1,45 @@ +// 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 CC_IPC_COPY_OUTPUT_REQUEST_STRUCT_TRAITS_H_ +#define CC_IPC_COPY_OUTPUT_REQUEST_STRUCT_TRAITS_H_ + +#include "cc/ipc/copy_output_request.mojom-shared.h" +#include "cc/output/copy_output_request.h" + +namespace mojo { + +template <> +struct StructTraits<cc::mojom::CopyOutputRequestDataView, + cc::CopyOutputRequest> { + static const base::Optional<base::UnguessableToken>& source( + const cc::CopyOutputRequest& request) { + return request.source_; + } + + static bool force_bitmap_result(const cc::CopyOutputRequest& request) { + return request.force_bitmap_result_; + } + + static const base::Optional<gfx::Rect>& area( + const cc::CopyOutputRequest& request) { + return request.area_; + } + + static const base::Optional<cc::TextureMailbox>& texture_mailbox( + const cc::CopyOutputRequest& request) { + return request.texture_mailbox_; + } + + static bool Read(cc::mojom::CopyOutputRequestDataView data, + cc::CopyOutputRequest* out) { + out->force_bitmap_result_ = data.force_bitmap_result(); + return data.ReadSource(&out->source_) && data.ReadArea(&out->area_) && + data.ReadTextureMailbox(&out->texture_mailbox_); + } +}; + +} // namespace mojo + +#endif // CC_IPC_COPY_OUTPUT_REQUEST_STRUCT_TRAITS_H_
diff --git a/cc/ipc/struct_traits_unittest.cc b/cc/ipc/struct_traits_unittest.cc index f5313cf..b84233b 100644 --- a/cc/ipc/struct_traits_unittest.cc +++ b/cc/ipc/struct_traits_unittest.cc
@@ -5,6 +5,7 @@ #include "base/message_loop/message_loop.h" #include "cc/input/selection.h" #include "cc/ipc/traits_test_service.mojom.h" +#include "cc/output/copy_output_result.h" #include "cc/quads/debug_border_draw_quad.h" #include "cc/quads/render_pass.h" #include "cc/quads/render_pass_draw_quad.h" @@ -50,6 +51,12 @@ callback.Run(c); } + void EchoCopyOutputRequest( + const CopyOutputRequest& c, + const EchoCopyOutputRequestCallback& callback) override { + callback.Run(std::move(c)); + } + void EchoFilterOperation( const FilterOperation& f, const EchoFilterOperationCallback& callback) override { @@ -116,6 +123,8 @@ DISALLOW_COPY_AND_ASSIGN(StructTraitsTest); }; +void StubCopyOutputRequestCallback(std::unique_ptr<CopyOutputResult> result) {} + } // namespace TEST_F(StructTraitsTest, BeginFrameArgs) { @@ -344,6 +353,48 @@ EXPECT_EQ(referenced_surfaces[i], output.referenced_surfaces[i]); } +TEST_F(StructTraitsTest, CopyOutputRequest) { + const gfx::Rect area(5, 7, 44, 55); + const auto callback = base::Bind(StubCopyOutputRequestCallback); + const int8_t mailbox_name[GL_MAILBOX_SIZE_CHROMIUM] = { + 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 9, 7, 5, 3, 1, 3}; + const uint32_t target = 3; + const auto source = + base::UnguessableToken::Deserialize(0xdeadbeef, 0xdeadf00d); + gpu::Mailbox mailbox; + mailbox.SetName(mailbox_name); + TextureMailbox texture_mailbox(mailbox, gpu::SyncToken(), target); + + // Test with bitmap. + std::unique_ptr<CopyOutputRequest> input; + input = CopyOutputRequest::CreateBitmapRequest(callback); + input->set_area(area); + input->set_source(source); + + mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); + CopyOutputRequest output; + proxy->EchoCopyOutputRequest(*input.get(), &output); + + EXPECT_TRUE(output.force_bitmap_result()); + EXPECT_FALSE(output.has_texture_mailbox()); + EXPECT_TRUE(output.has_area()); + EXPECT_EQ(area, output.area()); + EXPECT_EQ(source, output.source()); + + // Test with texture mailbox. + input = CopyOutputRequest::CreateRequest(callback); + input->SetTextureMailbox(texture_mailbox); + + CopyOutputRequest output2; + proxy->EchoCopyOutputRequest(*input.get(), &output2); + + EXPECT_TRUE(output2.has_texture_mailbox()); + EXPECT_FALSE(output2.has_area()); + EXPECT_EQ(mailbox, output2.texture_mailbox().mailbox()); + EXPECT_EQ(target, output2.texture_mailbox().target()); + EXPECT_FALSE(output2.has_source()); +} + TEST_F(StructTraitsTest, FilterOperation) { const FilterOperation inputs[] = { FilterOperation::CreateBlurFilter(20),
diff --git a/cc/ipc/traits_test_service.mojom b/cc/ipc/traits_test_service.mojom index b77c187c..4e0bb99 100644 --- a/cc/ipc/traits_test_service.mojom +++ b/cc/ipc/traits_test_service.mojom
@@ -7,6 +7,7 @@ import "cc/ipc/begin_frame_args.mojom"; import "cc/ipc/compositor_frame.mojom"; import "cc/ipc/compositor_frame_metadata.mojom"; +import "cc/ipc/copy_output_request.mojom"; import "cc/ipc/filter_operation.mojom"; import "cc/ipc/filter_operations.mojom"; import "cc/ipc/quads.mojom"; @@ -34,6 +35,10 @@ (CompositorFrameMetadata pass); [Sync] + EchoCopyOutputRequest(CopyOutputRequest c) => + (CopyOutputRequest pass); + + [Sync] EchoFilterOperation(FilterOperation f) => (FilterOperation pass); [Sync]
diff --git a/cc/ipc/typemaps.gni b/cc/ipc/typemaps.gni index 7c3dda2..a32c994 100644 --- a/cc/ipc/typemaps.gni +++ b/cc/ipc/typemaps.gni
@@ -6,6 +6,7 @@ "//cc/ipc/begin_frame_args.typemap", "//cc/ipc/compositor_frame.typemap", "//cc/ipc/compositor_frame_metadata.typemap", + "//cc/ipc/copy_output_request.typemap", "//cc/ipc/filter_operation.typemap", "//cc/ipc/filter_operations.typemap", "//cc/ipc/frame_sink_id.typemap",
diff --git a/cc/output/DEPS b/cc/output/DEPS new file mode 100644 index 0000000..7502d902 --- /dev/null +++ b/cc/output/DEPS
@@ -0,0 +1,3 @@ +include_rules = [ + "+mojo/public/cpp/bindings/struct_traits.h", +]
diff --git a/cc/output/copy_output_request.cc b/cc/output/copy_output_request.cc index 64ada7f6..4f1f4263 100644 --- a/cc/output/copy_output_request.cc +++ b/cc/output/copy_output_request.cc
@@ -20,24 +20,17 @@ const CopyOutputRequestCallback& result_callback) { std::unique_ptr<CopyOutputRequest> relay = CreateRequest(result_callback); relay->force_bitmap_result_ = original_request.force_bitmap_result_; - relay->has_area_ = original_request.has_area_; relay->area_ = original_request.area_; - relay->has_texture_mailbox_ = original_request.has_texture_mailbox_; relay->texture_mailbox_ = original_request.texture_mailbox_; return relay; } -CopyOutputRequest::CopyOutputRequest() - : force_bitmap_result_(false), - has_area_(false), - has_texture_mailbox_(false) {} +CopyOutputRequest::CopyOutputRequest() : force_bitmap_result_(false) {} CopyOutputRequest::CopyOutputRequest( bool force_bitmap_result, const CopyOutputRequestCallback& result_callback) : force_bitmap_result_(force_bitmap_result), - has_area_(false), - has_texture_mailbox_(false), result_callback_(result_callback) { DCHECK(!result_callback_.is_null()); TRACE_EVENT_ASYNC_BEGIN0("cc", "CopyOutputRequest", this); @@ -75,7 +68,6 @@ const TextureMailbox& texture_mailbox) { DCHECK(!force_bitmap_result_); DCHECK(texture_mailbox.IsTexture()); - has_texture_mailbox_ = true; texture_mailbox_ = texture_mailbox; }
diff --git a/cc/output/copy_output_request.h b/cc/output/copy_output_request.h index 6df8ba8..2bc0b4d 100644 --- a/cc/output/copy_output_request.h +++ b/cc/output/copy_output_request.h
@@ -14,11 +14,17 @@ #include "cc/base/cc_export.h" #include "cc/resources/single_release_callback.h" #include "cc/resources/texture_mailbox.h" +#include "mojo/public/cpp/bindings/struct_traits.h" #include "ui/gfx/geometry/rect.h" class SkBitmap; namespace cc { + +namespace mojom { +class CopyOutputRequestDataView; +} + class CopyOutputResult; class CC_EXPORT CopyOutputRequest { @@ -41,6 +47,8 @@ const CopyOutputRequest& original_request, const CopyOutputRequestCallback& result_callback); + CopyOutputRequest(); + ~CopyOutputRequest(); bool IsEmpty() const { return result_callback_.is_null(); } @@ -57,19 +65,16 @@ // By default copy requests copy the entire layer's subtree output. If an // area is given, then the intersection of this rect (in layer space) with // the layer's subtree output will be returned. - void set_area(const gfx::Rect& area) { - has_area_ = true; - area_ = area; - } - bool has_area() const { return has_area_; } - gfx::Rect area() const { return area_; } + void set_area(const gfx::Rect& area) { area_ = area; } + bool has_area() const { return area_.has_value(); } + const gfx::Rect& area() const { return *area_; } // By default copy requests create a new TextureMailbox to return contents // in. This allows a client to provide a TextureMailbox, and the compositor // will place the result inside the TextureMailbox. void SetTextureMailbox(const TextureMailbox& texture_mailbox); - bool has_texture_mailbox() const { return has_texture_mailbox_; } - const TextureMailbox& texture_mailbox() const { return texture_mailbox_; } + bool has_texture_mailbox() const { return texture_mailbox_.has_value(); } + const TextureMailbox& texture_mailbox() const { return *texture_mailbox_; } void SendEmptyResult(); void SendBitmapResult(std::unique_ptr<SkBitmap> bitmap); @@ -81,16 +86,16 @@ void SendResult(std::unique_ptr<CopyOutputResult> result); private: - CopyOutputRequest(); + friend struct mojo::StructTraits<mojom::CopyOutputRequestDataView, + CopyOutputRequest>; + CopyOutputRequest(bool force_bitmap_result, const CopyOutputRequestCallback& result_callback); base::Optional<base::UnguessableToken> source_; bool force_bitmap_result_; - bool has_area_; - bool has_texture_mailbox_; - gfx::Rect area_; - TextureMailbox texture_mailbox_; + base::Optional<gfx::Rect> area_; + base::Optional<TextureMailbox> texture_mailbox_; CopyOutputRequestCallback result_callback_; };
diff --git a/cc/tiles/picture_layer_tiling.cc b/cc/tiles/picture_layer_tiling.cc index 623ab3e..ce6b1d8f 100644 --- a/cc/tiles/picture_layer_tiling.cc +++ b/cc/tiles/picture_layer_tiling.cc
@@ -722,8 +722,10 @@ bool PictureLayerTiling::IsTileOccludedOnCurrentTree(const Tile* tile) const { if (!current_occlusion_in_layer_space_.HasOcclusion()) return false; + gfx::Rect tile_bounds = + tiling_data_.TileBounds(tile->tiling_i_index(), tile->tiling_j_index()); gfx::Rect tile_query_rect = - gfx::IntersectRects(tile->content_rect(), current_visible_rect_); + gfx::IntersectRects(tile_bounds, current_visible_rect_); // Explicitly check if the tile is outside the viewport. If so, we need to // return false, since occlusion for this tile is unknown. if (tile_query_rect.IsEmpty()) @@ -747,8 +749,17 @@ if (IsTileOccluded(tile)) return false; - bool tile_is_visible = - tile->content_rect().Intersects(current_visible_rect_); + // We may be checking the active tree tile here (since this function is also + // called for active trees below, ensure that this is at all a valid tile on + // the pending tree. + if (tile->tiling_i_index() >= tiling_data_.num_tiles_x() || + tile->tiling_j_index() >= tiling_data_.num_tiles_y()) { + return false; + } + + gfx::Rect tile_bounds = + tiling_data_.TileBounds(tile->tiling_i_index(), tile->tiling_j_index()); + bool tile_is_visible = tile_bounds.Intersects(current_visible_rect_); if (!tile_is_visible) return false; @@ -793,7 +804,9 @@ if (resolution_ != HIGH_RESOLUTION) return false; - bool tile_is_visible = current_visible_rect_.Intersects(tile->content_rect()); + gfx::Rect tile_bounds = + tiling_data_.TileBounds(tile->tiling_i_index(), tile->tiling_j_index()); + bool tile_is_visible = current_visible_rect_.Intersects(tile_bounds); if (!tile_is_visible) return false; @@ -803,7 +816,6 @@ } void PictureLayerTiling::UpdateRequiredStatesOnTile(Tile* tile) const { - DCHECK(tile); tile->set_required_for_activation(IsTileRequiredForActivation(tile)); tile->set_required_for_draw(IsTileRequiredForDraw(tile)); } @@ -815,7 +827,12 @@ DCHECK(raster_source()->CoversRect(tile->enclosing_layer_rect())) << "Tile layer rect: " << tile->enclosing_layer_rect().ToString(); + UpdateRequiredStatesOnTile(tile); const auto& tile_priority = ComputePriorityForTile(tile, priority_rect_type); + DCHECK((!tile->required_for_activation() && !tile->required_for_draw()) || + tile_priority.priority_bin == TilePriority::NOW || + !client_->HasValidTilePriorities()); + // Note that TileManager will consider this flag but may rasterize the tile // anyway (if tile is required for activation for example). We should process // the tile for images only if it's further than half of the skewport extent. @@ -833,7 +850,6 @@ std::map<const Tile*, PrioritizedTile> result; for (const auto& key_tile_pair : tiles_) { Tile* tile = key_tile_pair.second.get(); - UpdateRequiredStatesOnTile(tile); PrioritizedTile prioritized_tile = MakePrioritizedTile(tile, ComputePriorityRectTypeForTile(tile)); result.insert(std::make_pair(prioritized_tile.tile(), prioritized_tile));
diff --git a/cc/tiles/picture_layer_tiling.h b/cc/tiles/picture_layer_tiling.h index 962ae8d..16d7983 100644 --- a/cc/tiles/picture_layer_tiling.h +++ b/cc/tiles/picture_layer_tiling.h
@@ -157,8 +157,10 @@ } void UpdateAllRequiredStateForTesting() { - for (const auto& key_tile_pair : tiles_) - UpdateRequiredStatesOnTile(key_tile_pair.second.get()); + for (const auto& key_tile_pair : tiles_) { + Tile* tile = key_tile_pair.second.get(); + UpdateRequiredStatesOnTile(tile); + } } std::map<const Tile*, PrioritizedTile> UpdateAndGetAllPrioritizedTilesForTesting() const;
diff --git a/cc/tiles/picture_layer_tiling_unittest.cc b/cc/tiles/picture_layer_tiling_unittest.cc index 147f836..fdafb2d 100644 --- a/cc/tiles/picture_layer_tiling_unittest.cc +++ b/cc/tiles/picture_layer_tiling_unittest.cc
@@ -61,9 +61,13 @@ } gfx::Rect live_tiles_rect() const { return live_tiles_rect_; } + PriorityRectType visible_rect_type() const { + return PriorityRectType::VISIBLE_RECT; + } using PictureLayerTiling::RemoveTileAt; using PictureLayerTiling::RemoveTilesInRegion; + using PictureLayerTiling::ComputePriorityRectTypeForTile; protected: TestablePictureLayerTiling(WhichTree tree, @@ -955,6 +959,49 @@ EXPECT_FALSE(recycle_tiling->TileAt(0, 0)); } +TEST(PictureLayerTilingTest, EdgeCaseTileNowAndRequired) { + FakePictureLayerTilingClient pending_client; + pending_client.SetTileSize(gfx::Size(100, 100)); + + scoped_refptr<FakeRasterSource> raster_source = + FakeRasterSource::CreateFilled(gfx::Size(500, 500)); + std::unique_ptr<TestablePictureLayerTiling> pending_tiling = + TestablePictureLayerTiling::Create(PENDING_TREE, 1.0f, raster_source, + &pending_client, LayerTreeSettings()); + pending_tiling->set_resolution(HIGH_RESOLUTION); + pending_tiling->set_can_require_tiles_for_activation(true); + + // The tile at (1, 0) should be touching the visible rect, but not + // intersecting it. + gfx::Rect visible_rect = gfx::Rect(0, 0, 99, 99); + gfx::Rect eventually_rect = gfx::Rect(0, 0, 500, 500); + pending_tiling->ComputeTilePriorityRects(visible_rect, visible_rect, + visible_rect, eventually_rect, 1.f, + Occlusion()); + + Tile* tile = pending_tiling->TileAt(1, 0); + EXPECT_NE(pending_tiling->visible_rect_type(), + pending_tiling->ComputePriorityRectTypeForTile(tile)); + EXPECT_FALSE(pending_tiling->IsTileRequiredForActivation(tile)); + EXPECT_TRUE(tile->content_rect().Intersects(visible_rect)); + EXPECT_FALSE(pending_tiling->tiling_data() + ->TileBounds(tile->tiling_i_index(), tile->tiling_j_index()) + .Intersects(visible_rect)); + + // Now the tile at (1, 0) should be intersecting the visible rect. + visible_rect = gfx::Rect(0, 0, 100, 100); + pending_tiling->ComputeTilePriorityRects(visible_rect, visible_rect, + visible_rect, eventually_rect, 1.f, + Occlusion()); + EXPECT_EQ(pending_tiling->visible_rect_type(), + pending_tiling->ComputePriorityRectTypeForTile(tile)); + EXPECT_TRUE(pending_tiling->IsTileRequiredForActivation(tile)); + EXPECT_TRUE(tile->content_rect().Intersects(visible_rect)); + EXPECT_TRUE(pending_tiling->tiling_data() + ->TileBounds(tile->tiling_i_index(), tile->tiling_j_index()) + .Intersects(visible_rect)); +} + TEST_F(PictureLayerTilingIteratorTest, ResizeTilesAndUpdateToCurrent) { // The tiling has four rows and three columns. Initialize(gfx::Size(150, 100), 1.f, gfx::Size(250, 150));
diff --git a/cc/tiles/tiling_set_eviction_queue.cc b/cc/tiles/tiling_set_eviction_queue.cc index 45bab03..da09e31 100644 --- a/cc/tiles/tiling_set_eviction_queue.cc +++ b/cc/tiles/tiling_set_eviction_queue.cc
@@ -232,7 +232,6 @@ if (tiling->pending_visible_rect().Intersects(tile_rect)) return false; } - (*tilings_)[tiling_index_]->UpdateRequiredStatesOnTile(tile); prioritized_tile_ = (*tilings_)[tiling_index_]->MakePrioritizedTile( tile, priority_rect_type_); // In other cases, the tile we got is a viable candidate, return true.
diff --git a/cc/tiles/tiling_set_raster_queue_all.cc b/cc/tiles/tiling_set_raster_queue_all.cc index 1d88150..74bf1b2 100644 --- a/cc/tiles/tiling_set_raster_queue_all.cc +++ b/cc/tiles/tiling_set_raster_queue_all.cc
@@ -180,7 +180,6 @@ } Tile* tile = tiling_->TileAt(iterator->index_x(), iterator->index_y()); if (IsTileValid(tile)) { - tiling_->UpdateRequiredStatesOnTile(tile); current_tile_ = tiling_->MakePrioritizedTile(tile, priority_rect_type_); break; } @@ -195,7 +194,6 @@ current_tile_ = PrioritizedTile(); return false; } - tiling_->UpdateRequiredStatesOnTile(tile); current_tile_ = tiling_->MakePrioritizedTile(tile, priority_rect_type_); return true; } @@ -208,9 +206,9 @@ // for pending visible rect tiles as tiling iterators do not ignore those // tiles. if (priority_rect_type_ > PictureLayerTiling::PENDING_VISIBLE_RECT) { - gfx::Rect tile_rect = tiling_->tiling_data()->TileBounds( - tile->tiling_i_index(), tile->tiling_j_index()); - if (pending_visible_rect_.Intersects(tile_rect)) + gfx::Rect tile_bounds = tiling_data_->TileBounds(tile->tiling_i_index(), + tile->tiling_j_index()); + if (pending_visible_rect_.Intersects(tile_bounds)) return false; } return true;
diff --git a/cc/tiles/tiling_set_raster_queue_required.cc b/cc/tiles/tiling_set_raster_queue_required.cc index b0173c9..412942c9 100644 --- a/cc/tiles/tiling_set_raster_queue_required.cc +++ b/cc/tiles/tiling_set_raster_queue_required.cc
@@ -105,7 +105,6 @@ // for occlusion, since the tile's internal state has not yet been updated. if (tile && tile->draw_info().NeedsRaster() && !tiling_->IsTileOccluded(tile)) { - tiling_->UpdateRequiredStatesOnTile(tile); current_tile_ = tiling_->MakePrioritizedTile( tile, tiling_->ComputePriorityRectTypeForTile(tile)); return; @@ -145,7 +144,6 @@ break; } - tiling_->UpdateRequiredStatesOnTile(tile); current_tile_ = tiling_->MakePrioritizedTile( tile, tiling_->ComputePriorityRectTypeForTile(tile)); return *this;
diff --git a/chrome/android/java/res/drawable-hdpi/list_item_bottom.9.png b/chrome/android/java/res/drawable-hdpi/list_item_bottom.9.png new file mode 100644 index 0000000..f77d168 --- /dev/null +++ b/chrome/android/java/res/drawable-hdpi/list_item_bottom.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/list_item_middle.9.png b/chrome/android/java/res/drawable-hdpi/list_item_middle.9.png new file mode 100644 index 0000000..44204aee --- /dev/null +++ b/chrome/android/java/res/drawable-hdpi/list_item_middle.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/list_item_single.9.png b/chrome/android/java/res/drawable-hdpi/list_item_single.9.png new file mode 100644 index 0000000..73e2249 --- /dev/null +++ b/chrome/android/java/res/drawable-hdpi/list_item_single.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/list_item_top.9.png b/chrome/android/java/res/drawable-hdpi/list_item_top.9.png new file mode 100644 index 0000000..e873221b9 --- /dev/null +++ b/chrome/android/java/res/drawable-hdpi/list_item_top.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/list_item_bottom.9.png b/chrome/android/java/res/drawable-mdpi/list_item_bottom.9.png new file mode 100644 index 0000000..3a5295e --- /dev/null +++ b/chrome/android/java/res/drawable-mdpi/list_item_bottom.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/list_item_middle.9.png b/chrome/android/java/res/drawable-mdpi/list_item_middle.9.png new file mode 100644 index 0000000..4e07e11 --- /dev/null +++ b/chrome/android/java/res/drawable-mdpi/list_item_middle.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/list_item_single.9.png b/chrome/android/java/res/drawable-mdpi/list_item_single.9.png new file mode 100644 index 0000000..0d365d0 --- /dev/null +++ b/chrome/android/java/res/drawable-mdpi/list_item_single.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/list_item_top.9.png b/chrome/android/java/res/drawable-mdpi/list_item_top.9.png new file mode 100644 index 0000000..672498d --- /dev/null +++ b/chrome/android/java/res/drawable-mdpi/list_item_top.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/list_item_bottom.9.png b/chrome/android/java/res/drawable-xhdpi/list_item_bottom.9.png new file mode 100644 index 0000000..ad84d13 --- /dev/null +++ b/chrome/android/java/res/drawable-xhdpi/list_item_bottom.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/list_item_middle.9.png b/chrome/android/java/res/drawable-xhdpi/list_item_middle.9.png new file mode 100644 index 0000000..a1344e4 --- /dev/null +++ b/chrome/android/java/res/drawable-xhdpi/list_item_middle.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/list_item_single.9.png b/chrome/android/java/res/drawable-xhdpi/list_item_single.9.png new file mode 100644 index 0000000..f3b2271 --- /dev/null +++ b/chrome/android/java/res/drawable-xhdpi/list_item_single.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/list_item_top.9.png b/chrome/android/java/res/drawable-xhdpi/list_item_top.9.png new file mode 100644 index 0000000..99b0a86 --- /dev/null +++ b/chrome/android/java/res/drawable-xhdpi/list_item_top.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/list_item_bottom.9.png b/chrome/android/java/res/drawable-xxhdpi/list_item_bottom.9.png new file mode 100644 index 0000000..43ee2677 --- /dev/null +++ b/chrome/android/java/res/drawable-xxhdpi/list_item_bottom.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/list_item_middle.9.png b/chrome/android/java/res/drawable-xxhdpi/list_item_middle.9.png new file mode 100644 index 0000000..aa75eda --- /dev/null +++ b/chrome/android/java/res/drawable-xxhdpi/list_item_middle.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/list_item_single.9.png b/chrome/android/java/res/drawable-xxhdpi/list_item_single.9.png new file mode 100644 index 0000000..c422897 --- /dev/null +++ b/chrome/android/java/res/drawable-xxhdpi/list_item_single.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/list_item_top.9.png b/chrome/android/java/res/drawable-xxhdpi/list_item_top.9.png new file mode 100644 index 0000000..5bfc1d5 --- /dev/null +++ b/chrome/android/java/res/drawable-xxhdpi/list_item_top.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/list_item_bottom.9.png b/chrome/android/java/res/drawable-xxxhdpi/list_item_bottom.9.png new file mode 100644 index 0000000..d5aafce --- /dev/null +++ b/chrome/android/java/res/drawable-xxxhdpi/list_item_bottom.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/list_item_middle.9.png b/chrome/android/java/res/drawable-xxxhdpi/list_item_middle.9.png new file mode 100644 index 0000000..02be550 --- /dev/null +++ b/chrome/android/java/res/drawable-xxxhdpi/list_item_middle.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/list_item_single.9.png b/chrome/android/java/res/drawable-xxxhdpi/list_item_single.9.png new file mode 100644 index 0000000..88f61fe --- /dev/null +++ b/chrome/android/java/res/drawable-xxxhdpi/list_item_single.9.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/list_item_top.9.png b/chrome/android/java/res/drawable-xxxhdpi/list_item_top.9.png new file mode 100644 index 0000000..eafa88ba --- /dev/null +++ b/chrome/android/java/res/drawable-xxxhdpi/list_item_top.9.png Binary files differ
diff --git a/chrome/android/java/res/layout/bookmark_row_content.xml b/chrome/android/java/res/layout/bookmark_row_content.xml index e7bf8e9..d16453b 100644 --- a/chrome/android/java/res/layout/bookmark_row_content.xml +++ b/chrome/android/java/res/layout/bookmark_row_content.xml
@@ -20,7 +20,7 @@ android:layout_width="@dimen/selectable_list_layout_start_icon_width" android:layout_height="match_parent" android:contentDescription="@null" - android:scaleType="center" /> + android:scaleType="center"/> <TextView android:id="@+id/title" @@ -32,7 +32,8 @@ android:gravity="center_vertical" android:textAlignment="viewStart" android:textColor="@color/default_text_color" - android:textSize="16sp" /> + android:textSize="16sp" + android:paddingStart="16dp" /> <org.chromium.chrome.browser.widget.TintedImageButton android:id="@+id/more"
diff --git a/chrome/android/java/res/layout/bottom_control_container.xml b/chrome/android/java/res/layout/bottom_control_container.xml index b5ebc54a..a6ca2886 100644 --- a/chrome/android/java/res/layout/bottom_control_container.xml +++ b/chrome/android/java/res/layout/bottom_control_container.xml
@@ -20,7 +20,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView - android:id="@+id/toolbar_shadow" + android:id="@+id/bottom_toolbar_shadow" android:layout_width="match_parent" android:layout_height="@dimen/toolbar_shadow_height" android:src="@drawable/toolbar_shadow" @@ -48,4 +48,19 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> + <ViewStub + android:id="@+id/bottom_omnibox_results_container_stub" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inflatedId="@+id/omnibox_results_container" + android:layout="@layout/omnibox_results_container" /> + + <ImageView + android:id="@+id/toolbar_shadow" + android:src="@drawable/toolbar_shadow" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:scaleType="fitXY" + android:contentDescription="@null" /> + </org.chromium.chrome.browser.widget.BottomSheet>
diff --git a/chrome/android/java/res/layout/history_date_view.xml b/chrome/android/java/res/layout/history_date_view.xml new file mode 100644 index 0000000..d5d71839 --- /dev/null +++ b/chrome/android/java/res/layout/history_date_view.xml
@@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2017 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> + +<!-- TODO(twellington): merge with regular date_view.xml once UIs are unified. --> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/RobotoMediumStyle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="15dp" + android:layout_marginTop="17dp" + android:textSize="16sp" />
diff --git a/chrome/android/java/res/layout/history_header.xml b/chrome/android/java/res/layout/history_header.xml index c57ef18..51b940e1 100644 --- a/chrome/android/java/res/layout/history_header.xml +++ b/chrome/android/java/res/layout/history_header.xml
@@ -6,7 +6,8 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" > + android:orientation="vertical" + android:layout_marginBottom="8dp" > <org.chromium.ui.widget.TextViewWithClickableSpans android:id="@+id/signed_in_not_synced" @@ -24,8 +25,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" - android:layout_marginBottom="8dp" - android:background="@android:color/white" > + android:background="@drawable/list_item_single" > <Button android:id="@+id/clear_browsing_data_button"
diff --git a/chrome/android/java/res/layout/history_item_view.xml b/chrome/android/java/res/layout/history_item_view.xml index a38f855..55533d89 100644 --- a/chrome/android/java/res/layout/history_item_view.xml +++ b/chrome/android/java/res/layout/history_item_view.xml
@@ -15,8 +15,7 @@ android:layout_height="wrap_content" android:paddingTop="16dp" android:paddingBottom="16dp" - android:orientation="horizontal" - android:background="@android:color/white" > + android:orientation="horizontal" > <ImageView android:id="@+id/icon_view" @@ -28,7 +27,8 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" - android:orientation="vertical" > + android:orientation="vertical" + android:paddingStart="16dp" > <TextView android:id="@+id/title"
diff --git a/chrome/android/java/res/layout/main.xml b/chrome/android/java/res/layout/main.xml index a5ebb4c..40c4197 100644 --- a/chrome/android/java/res/layout/main.xml +++ b/chrome/android/java/res/layout/main.xml
@@ -20,11 +20,18 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> - <ViewStub - android:id="@+id/omnibox_results_container_stub" + <View + android:id="@+id/fading_focus_target" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="@dimen/tab_strip_height" + android:background="@color/omnibox_focused_fading_background_color" + android:visibility="gone" /> + + <ViewStub + android:id="@+id/omnibox_results_container_stub" + android:layout_width="match_parent" + android:layout_height="wrap_content" android:inflatedId="@+id/omnibox_results_container" android:layout="@layout/omnibox_results_container" />
diff --git a/chrome/android/java/res/layout/omnibox_results_container.xml b/chrome/android/java/res/layout/omnibox_results_container.xml index 1678f62..2430af6f 100644 --- a/chrome/android/java/res/layout/omnibox_results_container.xml +++ b/chrome/android/java/res/layout/omnibox_results_container.xml
@@ -8,4 +8,4 @@ android:visibility="gone" android:layout_width="match_parent" android:layout_height="match_parent" - /> \ No newline at end of file + />
diff --git a/chrome/android/java/res/layout/search_toolbar.xml b/chrome/android/java/res/layout/search_toolbar.xml index 843be256..0e4bd63 100644 --- a/chrome/android/java/res/layout/search_toolbar.xml +++ b/chrome/android/java/res/layout/search_toolbar.xml
@@ -29,7 +29,7 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:paddingStart="16dp" - android:paddingEnd="16dp" + android:paddingEnd="12dp" android:src="@drawable/btn_delete_url" android:background="@null" android:scaleType="center"
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml index ffad8d6..00b2b36 100644 --- a/chrome/android/java/res/values-v17/styles.xml +++ b/chrome/android/java/res/values-v17/styles.xml
@@ -576,8 +576,6 @@ <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> <item name="android:layout_marginTop">16dp</item> - <item name="android:paddingStart">16dp</item> - <item name="android:paddingEnd">16dp</item> <item name="android:textSize">16sp</item> <item name="android:visibility">gone</item> </style> @@ -597,9 +595,9 @@ <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">match_parent</item> <item name="android:paddingStart">16dp</item> - <item name="android:paddingEnd">16dp</item> <item name="android:background">@null</item> <item name="android:scaleType">center</item> + <item name="android:paddingEnd">@dimen/selectable_list_layout_row_end_padding</item> </style> <!-- Download Home -->
diff --git a/chrome/android/java/res/values/colors.xml b/chrome/android/java/res/values/colors.xml index 9f684e85..375ac06 100644 --- a/chrome/android/java/res/values/colors.xml +++ b/chrome/android/java/res/values/colors.xml
@@ -140,6 +140,7 @@ <color name="locationbar_status_color_light">#ffffff</color> <color name="locationbar_status_separator_color">#3d000000</color> <color name="locationbar_status_separator_color_light">#3dffffff</color> + <color name="omnibox_focused_fading_background_color">#a6000000</color> <!-- Find in Page colors --> <color name="find_result_bar_background_color">#bfffffff</color>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml index 35b1f96..bb18753 100644 --- a/chrome/android/java/res/values/dimens.xml +++ b/chrome/android/java/res/values/dimens.xml
@@ -290,7 +290,6 @@ (https://crbug.com/660837). --> <dimen name="snippets_peeking_card_peek_amount">32dp</dimen> <dimen name="snippets_peeking_card_bounce_distance">10dp</dimen> - <dimen name="snippets_card_corner_radius">2dp</dimen> <dimen name="snippets_card_gap">0.5dp</dimen> <dimen name="snippets_article_header_height">40dp</dimen> <dimen name="snippets_publisher_margin_top_with_article_snippet">16dp</dimen> @@ -393,8 +392,15 @@ <dimen name="downloads_item_icon_size">48dp</dimen> <dimen name="downloads_item_margin">16dp</dimen> + <!-- SelectableListLayout styles --> + <dimen name="selectable_list_layout_end_icon">24dp</dimen> + <dimen name="selectable_list_layout_row_end_padding">16dp</dimen> + <dimen name="selectable_list_layout_start_icon_width">56dp</dimen> + <dimen name="toolbar_wide_display_nav_icon_offset">-18dp</dimen> + <dimen name="toolbar_wide_display_end_offset">-14dp</dimen> + <!-- Miscellaneous dimensions --> <dimen name="action_bar_shadow_height">10dp</dimen> - <dimen name="selectable_list_layout_end_icon">24dp</dimen> - <dimen name="selectable_list_layout_start_icon_width">64dp</dimen> + <dimen name="card_corner_radius">2dp</dimen> + <dimen name="history_default_text_margin">16dp</dimen> </resources>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java index 2a1e095..5cd73bf 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
@@ -91,6 +91,11 @@ protected void attachBaseContext(Context base) { super.attachBaseContext(base); ContextUtils.initApplicationContext(this); + + // This field may be used before onCreate is called, so we need to create it here. + // It may be wise to update this anticipated capacity from time time as expectations for + // the quantity of downsteam-specific implementations increases (or decreases). + mImplementationMap = new ArrayMap<Class, Class>(8); } /** @@ -116,9 +121,6 @@ super.onCreate(); TraceEvent.end("ChromeApplication.onCreate"); - // It may be wise to update this anticipated capacity from time time as expectations for - // the quantity of downsteam-specific implementations increases (or decreases). - mImplementationMap = new ArrayMap<Class, Class>(8); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java index 45dbe5fb..def161d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -113,6 +113,7 @@ import org.chromium.chrome.browser.util.IntentUtils; import org.chromium.chrome.browser.vr_shell.VrShellDelegate; import org.chromium.chrome.browser.webapps.ChromeWebApkHost; +import org.chromium.chrome.browser.widget.BottomSheet; import org.chromium.chrome.browser.widget.emptybackground.EmptyBackgroundViewWrapper; import org.chromium.chrome.browser.widget.findinpage.FindToolbarManager; import org.chromium.content.browser.ContentVideoView; @@ -1367,6 +1368,13 @@ return true; } + // Close the bottom sheet before trying to navigate back. + if (getBottomSheet() != null + && getBottomSheet().getSheetState() != BottomSheet.SHEET_STATE_PEEK) { + getBottomSheet().setSheetState(BottomSheet.SHEET_STATE_PEEK, true); + return true; + } + if (getToolbarManager().back()) { recordBackPressedUma("Navigating backward", BACK_PRESSED_NAVIGATED_BACK); RecordUserAction.record("MobileTabClobbered");
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java index 368ea70..0a7aece 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
@@ -693,13 +693,14 @@ || intent.hasCategory(Intent.CATEGORY_DEFAULT) || intent.getCategories() == null)) { String lowerCaseScheme = scheme.toLowerCase(Locale.US); - if ("chrome".equals(lowerCaseScheme) || "chrome-native".equals(lowerCaseScheme) - || "about".equals(lowerCaseScheme)) { + if (UrlConstants.CHROME_SCHEME.equals(lowerCaseScheme) + || UrlConstants.CHROME_NATIVE_SCHEME.equals(lowerCaseScheme) + || UrlConstants.ABOUT_SCHEME.equals(lowerCaseScheme)) { // Allow certain "safe" internal URLs to be launched by external // applications. String lowerCaseUrl = url.toLowerCase(Locale.US); - if ("about:blank".equals(lowerCaseUrl) - || "about://blank".equals(lowerCaseUrl)) { + if (UrlConstants.ABOUT_BLANK_DISPLAY_URL.equals(lowerCaseUrl) + || UrlConstants.ABOUT_BLANK_URL.equals(lowerCaseUrl)) { return false; } @@ -830,8 +831,9 @@ } private boolean isInvalidScheme(String scheme) { - return scheme != null && (scheme.toLowerCase(Locale.US).equals("javascript") - || scheme.toLowerCase(Locale.US).equals("jar")); + return scheme != null + && (scheme.toLowerCase(Locale.US).equals(UrlConstants.JAVASCRIPT_SCHEME) + || scheme.toLowerCase(Locale.US).equals(UrlConstants.JAR_SCHEME)); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/UrlConstants.java b/chrome/android/java/src/org/chromium/chrome/browser/UrlConstants.java index f6f5bcd7..1373af5 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/UrlConstants.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/UrlConstants.java
@@ -8,42 +8,62 @@ * Java side version of chrome/common/url_constants.cc */ public class UrlConstants { - public static final String CHROME_SCHEME = "chrome://"; - public static final String CHROME_NATIVE_SCHEME = "chrome-native://"; - public static final String CONTENT_SCHEME = "content://"; + public static final String ABOUT_SCHEME = "about"; + public static final String CHROME_SCHEME = "chrome"; + public static final String CHROME_NATIVE_SCHEME = "chrome-native"; + public static final String CONTENT_SCHEME = "content"; public static final String CUSTOM_TAB_SCHEME = "customtab"; + public static final String DATA_SCHEME = "data"; public static final String DOCUMENT_SCHEME = "document"; - public static final String FILE_SCHEME = "file://"; - public static final String HTTP_SCHEME = "http://"; - public static final String HTTPS_SCHEME = "https://"; + public static final String FILE_SCHEME = "file"; + public static final String FTP_SCHEME = "ftp"; + public static final String HTTP_SCHEME = "http"; + public static final String HTTPS_SCHEME = "https"; + public static final String INLINE_SCHEME = "inline"; + public static final String JAR_SCHEME = "jar"; + public static final String JAVASCRIPT_SCHEME = "javascript"; - public static final String NTP_URL = "chrome-native://newtab/"; + public static final String ABOUT_URL_SHORT_PREFIX = "about:"; + public static final String CONTENT_URL_SHORT_PREFIX = "content:"; + public static final String CHROME_URL_SHORT_PREFIX = "chrome:"; + public static final String CHROME_NATIVE_URL_SHORT_PREFIX = "chrome-native:"; + public static final String FILE_URL_SHORT_PREFIX = "file:"; + + public static final String CHROME_URL_PREFIX = "chrome://"; + public static final String CHROME_NATIVE_URL_PREFIX = "chrome-native://"; + public static final String CONTENT_URL_PREFIX = "content://"; + public static final String FILE_URL_PREFIX = "file://"; + public static final String HTTP_URL_PREFIX = "http://"; + public static final String HTTPS_URL_PREFIX = "https://"; + public static final String NTP_HOST = "newtab"; + public static final String NTP_URL = "chrome-native://newtab/"; + public static final String BOOKMARKS_HOST = "bookmarks"; public static final String BOOKMARKS_URL = "chrome-native://bookmarks/"; public static final String BOOKMARKS_FOLDER_URL = "chrome-native://bookmarks/folder/"; - public static final String - BOOKMARKS_UNCATEGORIZED_URL = "chrome-native://bookmarks/uncategorized/"; - public static final String BOOKMARKS_HOST = "bookmarks"; + public static final String BOOKMARKS_UNCATEGORIZED_URL = + "chrome-native://bookmarks/uncategorized/"; + public static final String DOWNLOADS_HOST = "downloads"; public static final String DOWNLOADS_URL = "chrome-native://downloads/"; public static final String DOWNLOADS_FILTER_URL = "chrome-native://downloads/filter/"; - public static final String DOWNLOADS_HOST = "downloads"; - public static final String RECENT_TABS_URL = "chrome-native://recent-tabs/"; public static final String RECENT_TABS_HOST = "recent-tabs"; + public static final String RECENT_TABS_URL = "chrome-native://recent-tabs/"; + public static final String HISTORY_HOST = "history"; public static final String HISTORY_URL = "chrome://history/"; public static final String NATIVE_HISTORY_URL = "chrome-native://history/"; - public static final String HISTORY_HOST = "history"; - public static final String INTERESTS_URL = "chrome-native://interests/"; public static final String INTERESTS_HOST = "interests"; + public static final String INTERESTS_URL = "chrome-native://interests/"; public static final String PHYSICAL_WEB_DIAGNOSTICS_HOST = "physical-web-diagnostics"; public static final String PHYSICAL_WEB_URL = "chrome://physical-web/"; - public static final String ABOUT_BLANK = "about:blank"; + public static final String ABOUT_BLANK_DISPLAY_URL = "about:blank"; + public static final String ABOUT_BLANK_URL = "about://blank"; public static final String GOOGLE_ACCOUNT_ACTIVITY_CONTROLS_URL = "https://myaccount.google.com/activitycontrols/search";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java index cf383453..d72051fd 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java
@@ -90,10 +90,10 @@ if (isPageMenu && currentTab != null) { String url = currentTab.getUrl(); - boolean isChromeScheme = url.startsWith(UrlConstants.CHROME_SCHEME) - || url.startsWith(UrlConstants.CHROME_NATIVE_SCHEME); - boolean isFileScheme = url.startsWith(UrlConstants.FILE_SCHEME); - boolean isContentScheme = url.startsWith(UrlConstants.CONTENT_SCHEME); + boolean isChromeScheme = url.startsWith(UrlConstants.CHROME_URL_PREFIX) + || url.startsWith(UrlConstants.CHROME_NATIVE_URL_PREFIX); + boolean isFileScheme = url.startsWith(UrlConstants.FILE_URL_PREFIX); + boolean isContentScheme = url.startsWith(UrlConstants.CONTENT_URL_PREFIX); boolean shouldShowIconRow = !mActivity.isTablet() || mActivity.getWindow().getDecorView().getWidth() < DeviceFormFactor.getMinimumTabletWidthPx(mActivity); @@ -299,8 +299,8 @@ protected void updateRequestDesktopSiteMenuItem( MenuItem requstMenuItem, Tab currentTab) { String url = currentTab.getUrl(); - boolean isChromeScheme = url.startsWith(UrlConstants.CHROME_SCHEME) - || url.startsWith(UrlConstants.CHROME_NATIVE_SCHEME); + boolean isChromeScheme = url.startsWith(UrlConstants.CHROME_URL_PREFIX) + || url.startsWith(UrlConstants.CHROME_NATIVE_URL_PREFIX); requstMenuItem.setVisible(!isChromeScheme || currentTab.isNativePage()); requstMenuItem.setChecked(currentTab.getUseDesktopUserAgent()); requstMenuItem.setTitleCondensed(requstMenuItem.isChecked()
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerDocument.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerDocument.java index bd21348c..13e06898 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerDocument.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerDocument.java
@@ -257,7 +257,7 @@ if (layoutTab == null) return; String url = tab.getUrl(); - boolean isNativePage = url != null && url.startsWith(UrlConstants.CHROME_NATIVE_SCHEME); + boolean isNativePage = url != null && url.startsWith(UrlConstants.CHROME_NATIVE_URL_PREFIX); int themeColor = tab.getThemeColor(); boolean canUseLiveTexture =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java index ec6fb23..af2153a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java
@@ -140,8 +140,8 @@ downloadItem.setEnabled(DownloadUtils.isAllowedToDownloadPage(currentTab)); String url = currentTab.getUrl(); - boolean isChromeScheme = url.startsWith(UrlConstants.CHROME_SCHEME) - || url.startsWith(UrlConstants.CHROME_NATIVE_SCHEME); + boolean isChromeScheme = url.startsWith(UrlConstants.CHROME_URL_PREFIX) + || url.startsWith(UrlConstants.CHROME_NATIVE_URL_PREFIX); if (isChromeScheme) { addToHomeScreenItem.setVisible(false); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java index 1dd1b41..eb44cee 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
@@ -14,6 +14,7 @@ import android.support.v4.widget.DrawerLayout; import android.support.v4.widget.DrawerLayout.DrawerListener; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.ItemAnimator; import android.support.v7.widget.Toolbar.OnMenuItemClickListener; import android.view.Gravity; import android.view.LayoutInflater; @@ -172,6 +173,8 @@ private final SpaceDisplay mSpaceDisplay; private final ListView mFilterView; private final UndoDeletionSnackbarController mUndoDeletionSnackbarController; + private final RecyclerView mRecyclerView; + private final ItemAnimator mItemAnimator; private BasicNativePage mNativePage; private Activity mActivity; @@ -203,10 +206,11 @@ R.string.download_manager_ui_empty); mHistoryAdapter = new DownloadHistoryAdapter(isOffTheRecord, parentComponent); - RecyclerView recyclerView = mSelectableListLayout.initializeRecyclerView(mHistoryAdapter); + mRecyclerView = mSelectableListLayout.initializeRecyclerView(mHistoryAdapter); + mItemAnimator = mRecyclerView.getItemAnimator(); // Prevent every progress update from causing a transition animation. - recyclerView.getItemAnimator().setChangeDuration(0); + mItemAnimator.setChangeDuration(0); mHistoryAdapter.initialize(mBackendProvider); addObserver(mHistoryAdapter); @@ -306,6 +310,7 @@ shareSelectedItems(); return true; } else if (item.getItemId() == R.id.search_menu_id) { + mRecyclerView.setItemAnimator(null); mToolbar.showSearchView(); mSelectableListLayout.setEmptyViewText(R.string.download_manager_no_results); RecordUserAction.record("Android.DownloadManager.Search"); @@ -365,6 +370,7 @@ /** Called when the filter has been changed by the user. */ void onFilterChanged(int filter) { mBackendProvider.getSelectionDelegate().clearSelection(); + mToolbar.hideSearchView(); for (DownloadUiObserver observer : mObservers) { observer.onFilterChanged(filter); @@ -387,6 +393,7 @@ public void onEndSearch() { mSelectableListLayout.setEmptyViewText(R.string.download_manager_ui_empty); mHistoryAdapter.onEndSearch(); + mRecyclerView.setItemAnimator(mItemAnimator); } private void shareSelectedItems() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java index db717ad..0273b15 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
@@ -34,6 +34,7 @@ import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeTabbedActivity2; import org.chromium.chrome.browser.IntentHandler; +import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.document.ChromeLauncherActivity; import org.chromium.chrome.browser.externalnav.ExternalNavigationHandler.OverrideUrlLoadingResult; import org.chromium.chrome.browser.instantapps.AuthenticatedProxyActivity; @@ -421,7 +422,7 @@ // If the url points inside of Chromium's data directory, no permissions are necessary. // This is required to prevent permission prompt when uses wants to access offline pages. - if (url.startsWith("file://" + PathUtils.getDataDirectory())) { + if (url.startsWith(UrlConstants.FILE_URL_PREFIX + PathUtils.getDataDirectory())) { return false; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java index 676e08a..55ba8d5 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java
@@ -43,9 +43,9 @@ */ public class ExternalNavigationHandler { private static final String TAG = "UrlHandler"; - private static final String SCHEME_WTAI = "wtai://wp/"; - private static final String SCHEME_WTAI_MC = "wtai://wp/mc;"; - private static final String SCHEME_SMS = "sms"; + private static final String WTAI_URL_PREFIX = "wtai://wp/"; + private static final String WTAI_MC_URL_PREFIX = "wtai://wp/mc;"; + private static final String SMS_SCHEME = "sms"; private static final String PLAY_PACKAGE_PARAM = "id"; private static final String PLAY_REFERRER_PARAM = "referrer"; @@ -211,7 +211,7 @@ // If accessing a file URL, ensure that the user has granted the necessary file access // to Chrome. This check should happen for reloads, navigations, etc..., which is why // it occurs before the subsequent blocks. - if (params.getUrl().startsWith("file:") + if (params.getUrl().startsWith(UrlConstants.FILE_URL_SHORT_PREFIX) && mDelegate.shouldRequestFileAccess(params.getUrl(), params.getTab())) { mDelegate.startFileIntent( intent, params.getReferrerUrl(), params.getTab(), @@ -274,36 +274,37 @@ // Don't override navigation from a chrome:* url to http or https. For example, // when clicking a link in bookmarks or most visited. When navigating from such a // page, there is clear intent to complete the navigation in Chrome. - if (params.getReferrerUrl() != null && params.getReferrerUrl().startsWith( - UrlConstants.CHROME_SCHEME) && (params.getUrl().startsWith(UrlConstants.HTTP_SCHEME) - || params.getUrl().startsWith(UrlConstants.HTTPS_SCHEME))) { + if (params.getReferrerUrl() != null + && params.getReferrerUrl().startsWith(UrlConstants.CHROME_URL_PREFIX) + && (params.getUrl().startsWith(UrlConstants.HTTP_URL_PREFIX) + || params.getUrl().startsWith(UrlConstants.HTTPS_URL_PREFIX))) { return OverrideUrlLoadingResult.NO_OVERRIDE; } - if (params.getUrl().startsWith(SCHEME_WTAI_MC)) { + if (params.getUrl().startsWith(WTAI_MC_URL_PREFIX)) { // wtai://wp/mc;number // number=string(phone-number) mDelegate.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(WebView.SCHEME_TEL - + params.getUrl().substring(SCHEME_WTAI_MC.length()))), false); + + params.getUrl().substring(WTAI_MC_URL_PREFIX.length()))), false); return OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT; } - if (params.getUrl().startsWith(SCHEME_WTAI)) { + if (params.getUrl().startsWith(WTAI_URL_PREFIX)) { // TODO: handle other WTAI schemes. return OverrideUrlLoadingResult.NO_OVERRIDE; } // The "about:", "chrome:", and "chrome-native:" schemes are internal to the browser; // don't want these to be dispatched to other apps. - if (params.getUrl().startsWith("about:") - || params.getUrl().startsWith("chrome:") - || params.getUrl().startsWith("chrome-native:")) { + if (params.getUrl().startsWith(UrlConstants.ABOUT_URL_SHORT_PREFIX) + || params.getUrl().startsWith(UrlConstants.CHROME_URL_SHORT_PREFIX) + || params.getUrl().startsWith(UrlConstants.CHROME_NATIVE_URL_SHORT_PREFIX)) { return OverrideUrlLoadingResult.NO_OVERRIDE; } // The "content:" scheme is disabled in Clank. Do not try to start an activity. - if (params.getUrl().startsWith("content:")) { + if (params.getUrl().startsWith(UrlConstants.CONTENT_URL_SHORT_PREFIX)) { return OverrideUrlLoadingResult.NO_OVERRIDE; } @@ -361,7 +362,7 @@ if (intent.getPackage() == null) { final Uri uri = intent.getData(); - if (uri != null && SCHEME_SMS.equals(uri.getScheme())) { + if (uri != null && SMS_SCHEME.equals(uri.getScheme())) { intent.setPackage(getDefaultSmsPackageName(resolvingInfos)); } } @@ -624,7 +625,7 @@ * @return Whether the |url| could be handled by an external application on the system. */ public boolean canExternalAppHandleUrl(String url) { - if (url.startsWith(SCHEME_WTAI_MC)) return true; + if (url.startsWith(WTAI_MC_URL_PREFIX)) return true; try { Intent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); if (intent.getPackage() != null) return true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gsa/ContextReporter.java b/chrome/android/java/src/org/chromium/chrome/browser/gsa/ContextReporter.java index 394e0f4c..9585d550 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/gsa/ContextReporter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/gsa/ContextReporter.java
@@ -172,8 +172,8 @@ } String currentUrl = currentTab.getUrl(); - if (TextUtils.isEmpty(currentUrl) || !(currentUrl.startsWith(UrlConstants.HTTP_SCHEME) - || currentUrl.startsWith(UrlConstants.HTTPS_SCHEME))) { + if (TextUtils.isEmpty(currentUrl) || !(currentUrl.startsWith(UrlConstants.HTTP_URL_PREFIX) + || currentUrl.startsWith(UrlConstants.HTTPS_URL_PREFIX))) { reportStatus(STATUS_INVALID_SCHEME); Log.d(TAG, "Not reporting, URL scheme is invalid"); reportUsageEndedIfNecessary();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryActivity.java index 5d181ac..e22f235 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryActivity.java
@@ -19,7 +19,7 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mHistoryManager = new HistoryManager(this); + mHistoryManager = new HistoryManager(this, null); setContentView(mHistoryManager.getView()); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java index 70487af..da30057 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
@@ -4,6 +4,7 @@ package org.chromium.chrome.browser.history; +import android.content.res.Resources; import android.support.v7.widget.RecyclerView.ViewHolder; import android.text.SpannableString; import android.text.method.LinkMovementMethod; @@ -12,13 +13,17 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.Button; +import android.widget.FrameLayout; import android.widget.TextView; import org.chromium.base.ContextUtils; import org.chromium.base.VisibleForTesting; import org.chromium.chrome.R; import org.chromium.chrome.browser.history.HistoryProvider.BrowsingHistoryObserver; +import org.chromium.chrome.browser.preferences.PrefServiceBridge; import org.chromium.chrome.browser.widget.DateDividedAdapter; +import org.chromium.chrome.browser.widget.DateDividedAdapter.DateViewHolder; +import org.chromium.chrome.browser.widget.displaystyle.MarginResizer; import org.chromium.chrome.browser.widget.selection.SelectableItemViewHolder; import org.chromium.chrome.browser.widget.selection.SelectionDelegate; import org.chromium.chrome.browser.widget.selection.SelectionDelegate.SelectionObserver; @@ -42,12 +47,14 @@ private final SelectionDelegate<HistoryItem> mSelectionDelegate; private final HistoryProvider mHistoryProvider; - private final HistoryManager mManager; + private final HistoryManager mHistoryManager; + private final ArrayList<HistoryItemView> mItemViews; private TextView mSignedInNotSyncedTextView; private TextView mSignedInSyncedTextView; private TextView mOtherFormsOfBrowsingHistoryTextView; private Button mClearBrowsingDataButton; + private FrameLayout mClearBrowsingDataButtonContainer; private boolean mHasOtherFormsOfBrowsingData; private boolean mHasSyncedData; @@ -60,8 +67,7 @@ private boolean mClearOnNextQueryComplete; private long mNextQueryEndTime; private String mQueryText = EMPTY_QUERY; - - private final ArrayList<HistoryItemView> mItemViews; + private int mDefaultTextMargin; public HistoryAdapter(SelectionDelegate<HistoryItem> delegate, HistoryManager manager, HistoryProvider provider) { @@ -69,7 +75,7 @@ mSelectionDelegate = delegate; mHistoryProvider = provider; mHistoryProvider.setObserver(this); - mManager = manager; + mHistoryManager = manager; mItemViews = new ArrayList<>(); } @@ -155,6 +161,12 @@ */ public void removeItems() { mHistoryProvider.removeItems(); + + // TODO(twellington): this could be optimized by only setting the background for item views + // in a group that has changed. + for (HistoryItemView itemView : mItemViews) { + itemView.setBackgroundResourceForGroupPosition(); + } } /** @@ -164,6 +176,7 @@ for (HistoryItemView itemView : mItemViews) { itemView.onSignInStateChange(); } + updateClearBrowsingDataButtonVisibility(); } /** @@ -195,12 +208,12 @@ SelectableItemViewHolder<HistoryItem> holder = (SelectableItemViewHolder<HistoryItem>) current; holder.displayItem(item); - ((HistoryItemView) holder.itemView).setHistoryManager(mManager); + ((HistoryItemView) holder.itemView).setHistoryManager(mHistoryManager); } @Override protected int getTimedItemViewResId() { - return R.layout.date_view; + return R.layout.history_date_view; } @Override @@ -248,28 +261,43 @@ protected BasicViewHolder createHeader(ViewGroup parent) { ViewGroup v = (ViewGroup) LayoutInflater.from(parent.getContext()).inflate( R.layout.history_header, parent, false); + Resources resources = v.getResources(); mIsHeaderInflated = true; mClearBrowsingDataButton = (Button) v.findViewById(R.id.clear_browsing_data_button); mClearBrowsingDataButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - mManager.openClearBrowsingDataPreference(); + mHistoryManager.openClearBrowsingDataPreference(); } }); + mClearBrowsingDataButtonContainer = (FrameLayout) mClearBrowsingDataButton.getParent(); + MarginResizer.createWithViewAdapter(mClearBrowsingDataButtonContainer, + mHistoryManager.getSelectableListLayout().getUiConfig(), + mHistoryManager.getDefaultLateralListItemMarginPx(), 0); + updateClearBrowsingDataButtonVisibility(); mSignedInNotSyncedTextView = (TextView) v.findViewById(R.id.signed_in_not_synced); setPrivacyDisclaimerText(mSignedInNotSyncedTextView, R.string.android_history_no_synced_results, LEARN_MORE_LINK); + MarginResizer.createWithViewAdapter(mSignedInNotSyncedTextView, + mHistoryManager.getSelectableListLayout().getUiConfig(), + getDefaultTextMargin(resources), mHistoryManager.getListItemLateralShadowSizePx()); mSignedInSyncedTextView = (TextView) v.findViewById(R.id.signed_in_synced); setPrivacyDisclaimerText(mSignedInSyncedTextView, R.string.android_history_has_synced_results, LEARN_MORE_LINK); + MarginResizer.createWithViewAdapter(mSignedInSyncedTextView, + mHistoryManager.getSelectableListLayout().getUiConfig(), + getDefaultTextMargin(resources), mHistoryManager.getListItemLateralShadowSizePx()); mOtherFormsOfBrowsingHistoryTextView = (TextView) v.findViewById( R.id.other_forms_of_browsing_history); setPrivacyDisclaimerText(mOtherFormsOfBrowsingHistoryTextView, R.string.android_history_other_forms_of_history, GOOGLE_HISTORY_LINK); + MarginResizer.createWithViewAdapter(mOtherFormsOfBrowsingHistoryTextView, + mHistoryManager.getSelectableListLayout().getUiConfig(), + getDefaultTextMargin(resources), mHistoryManager.getListItemLateralShadowSizePx()); setPrivacyDisclaimerVisibility(); @@ -282,11 +310,21 @@ R.layout.indeterminate_progress_view, parent, false)); } + @Override + protected DateViewHolder createDateViewHolder(ViewGroup parent) { + DateViewHolder viewHolder = super.createDateViewHolder(parent); + MarginResizer.createWithViewAdapter(viewHolder.itemView, + mHistoryManager.getSelectableListLayout().getUiConfig(), + getDefaultTextMargin(parent.getResources()), + mHistoryManager.getListItemLateralShadowSizePx()); + return viewHolder; + } + private void setPrivacyDisclaimerText(TextView view, int stringId, final String url) { NoUnderlineClickableSpan link = new NoUnderlineClickableSpan() { @Override public void onClick(View view) { - mManager.openUrl(url, null, true); + mHistoryManager.openUrl(url, null, true); } }; SpannableString spannable = SpanApplier.applySpans( @@ -308,6 +346,20 @@ mHasOtherFormsOfBrowsingData ? View.VISIBLE : View.GONE); } + private void updateClearBrowsingDataButtonVisibility() { + mClearBrowsingDataButtonContainer.setVisibility( + !PrefServiceBridge.getInstance().canDeleteBrowsingHistory() ? View.GONE : + View.VISIBLE); + } + + private int getDefaultTextMargin(Resources resources) { + if (mDefaultTextMargin == 0) { + mDefaultTextMargin = resources.getDimensionPixelSize( + R.dimen.history_default_text_margin); + } + return mDefaultTextMargin; + } + @VisibleForTesting TextView getSignedInNotSyncedViewForTests() { return mSignedInNotSyncedTextView;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java index bccddaa..a2df59d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
@@ -21,6 +21,7 @@ import org.chromium.chrome.browser.preferences.PrefServiceBridge; import org.chromium.chrome.browser.widget.RoundedIconGenerator; import org.chromium.chrome.browser.widget.TintedImageButton; +import org.chromium.chrome.browser.widget.displaystyle.MarginResizer; import org.chromium.chrome.browser.widget.selection.SelectableItemView; /** @@ -32,6 +33,7 @@ private TintedImageButton mRemoveButton; private ImageView mIconImageView; private VectorDrawableCompat mBlockedVisitDrawable; + private View mContentView; private HistoryManager mHistoryManager; private final RoundedIconGenerator mIconGenerator; @@ -39,6 +41,7 @@ private final int mMinIconSize; private final int mDisplayedIconSize; private final int mCornerRadius; + private final int mEndPadding; private boolean mRemoveButtonVisible; @@ -53,6 +56,8 @@ getResources(), R.color.default_favicon_background_color); mIconGenerator = new RoundedIconGenerator(mDisplayedIconSize , mDisplayedIconSize, mCornerRadius, iconColor, textSize); + mEndPadding = context.getResources().getDimensionPixelSize( + R.dimen.selectable_list_layout_row_end_padding); } @Override @@ -61,6 +66,7 @@ mTitle = (TextView) findViewById(R.id.title); mDomain = (TextView) findViewById(R.id.domain); mIconImageView = (ImageView) findViewById(R.id.icon_view); + mContentView = findViewById(R.id.content); mRemoveButton = (TintedImageButton) findViewById(R.id.remove); mRemoveButton.setOnClickListener(new OnClickListener() { @Override @@ -97,6 +103,8 @@ mTitle.setTextColor( ApiCompatibilityUtils.getColor(getResources(), R.color.default_text_color)); } + + setBackgroundResourceForGroupPosition(); } /** @@ -108,6 +116,10 @@ mHistoryManager = manager; if (!getItem().wasBlockedVisit()) requestIcon(); + + MarginResizer.createWithViewAdapter(this, + mHistoryManager.getSelectableListLayout().getUiConfig(), + mHistoryManager.getDefaultLateralListItemMarginPx(), 0); } /** @@ -164,8 +176,35 @@ } private void updateRemoveButtonVisibility() { - mRemoveButton.setVisibility( - !PrefServiceBridge.getInstance().canDeleteBrowsingHistory() ? View.GONE : - mRemoveButtonVisible ? View.VISIBLE : View.INVISIBLE); + int removeButtonVisibility = + !PrefServiceBridge.getInstance().canDeleteBrowsingHistory() ? View.GONE + : mRemoveButtonVisible ? View.VISIBLE : View.INVISIBLE; + mRemoveButton.setVisibility(removeButtonVisibility); + + int endPadding = removeButtonVisibility == View.GONE ? mEndPadding : 0; + ApiCompatibilityUtils.setPaddingRelative(mContentView, + ApiCompatibilityUtils.getPaddingStart(mContentView), + mContentView.getPaddingTop(), endPadding, mContentView.getPaddingBottom()); + } + + /** + * Sets the background resource for this view using the item's positioning in its group. + */ + public void setBackgroundResourceForGroupPosition() { + int backgroundResource; + + boolean isLastInGroup = getItem().isLastInGroup(); + boolean isFirstInGroup = getItem().isFirstInGroup(); + if (!isLastInGroup && !isFirstInGroup) { + backgroundResource = R.drawable.list_item_middle; + } else if (!isLastInGroup) { + backgroundResource = R.drawable.list_item_top; + } else if (!isFirstInGroup) { + backgroundResource = R.drawable.list_item_bottom; + } else { + backgroundResource = R.drawable.list_item_single; + } + + setBackgroundResource(backgroundResource); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java index f8b37fd..eba4a91e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
@@ -9,12 +9,15 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.graphics.Rect; import android.net.Uri; import android.provider.Browser; +import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.support.graphics.drawable.VectorDrawableCompat; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.ItemAnimator; import android.support.v7.widget.RecyclerView.OnScrollListener; import android.support.v7.widget.Toolbar.OnMenuItemClickListener; import android.view.LayoutInflater; @@ -29,6 +32,7 @@ import org.chromium.base.metrics.RecordUserAction; import org.chromium.chrome.R; import org.chromium.chrome.browser.IntentHandler; +import org.chromium.chrome.browser.NativePage; import org.chromium.chrome.browser.document.ChromeLauncherActivity; import org.chromium.chrome.browser.favicon.LargeIconBridge; import org.chromium.chrome.browser.preferences.PreferencesLauncher; @@ -37,6 +41,7 @@ import org.chromium.chrome.browser.signin.SigninManager.SignInStateObserver; import org.chromium.chrome.browser.util.IntentUtils; import org.chromium.chrome.browser.widget.FadingShadowView; +import org.chromium.chrome.browser.widget.displaystyle.UiConfig; import org.chromium.chrome.browser.widget.selection.SelectableListLayout; import org.chromium.chrome.browser.widget.selection.SelectableListToolbar.SearchDelegate; import org.chromium.chrome.browser.widget.selection.SelectionDelegate; @@ -57,7 +62,11 @@ private static HistoryProvider sProviderForTests; + private final int mListItemLateralShadowSizePx; + private final int mDefaultLateralListItemMarginPx; + private final Activity mActivity; + private final boolean mIsDisplayedInNativePage; private final SelectableListLayout<HistoryItem> mSelectableListLayout; private final HistoryAdapter mHistoryAdapter; private final SelectionDelegate<HistoryItem> mSelectionDelegate; @@ -65,6 +74,7 @@ private final TextView mEmptyView; private final FadingShadowView mToolbarShadow; private final RecyclerView mRecyclerView; + private final ItemAnimator mItemAnimator; private LargeIconBridge mLargeIconBridge; private boolean mIsSearching; @@ -74,19 +84,25 @@ * @param activity The Activity associated with the HistoryManager. */ @SuppressWarnings("unchecked") // mSelectableListLayout - public HistoryManager(Activity activity) { + public HistoryManager(Activity activity, @Nullable NativePage nativePage) { mActivity = activity; + mIsDisplayedInNativePage = nativePage != null; mSelectionDelegate = new SelectionDelegate<>(); mSelectionDelegate.addObserver(this); mHistoryAdapter = new HistoryAdapter(mSelectionDelegate, this, sProviderForTests != null ? sProviderForTests : new BrowsingHistoryBridge()); + // 1. Create SelectableListLayout. mSelectableListLayout = (SelectableListLayout<HistoryItem>) LayoutInflater.from(activity).inflate( R.layout.history_main, null); - mRecyclerView = mSelectableListLayout.initializeRecyclerView(mHistoryAdapter); + // 2. Initialize RecyclerView. + mRecyclerView = mSelectableListLayout.initializeRecyclerView(mHistoryAdapter); + mItemAnimator = mRecyclerView.getItemAnimator(); + + // 3. Initialize toolbar. mToolbar = (HistoryManagerToolbar) mSelectableListLayout.initializeToolbar( R.layout.history_toolbar, mSelectionDelegate, R.string.menu_history, null, R.id.normal_menu_group, R.id.selection_mode_menu_group, @@ -96,6 +112,28 @@ mToolbarShadow = (FadingShadowView) mSelectableListLayout.findViewById(R.id.shadow); mToolbarShadow.setVisibility(View.GONE); + // 4. Configure values for {@link UiConfig#DISPLAY_STYLE_WIDE} and + // {@link UiConfig#DISPLAY_STYLE_REGULAR}. + // The list item shadow is part of the drawable nine-patch used as the list item background. + // Use the dimensions of the shadow (from the drawable's padding) to calculate the margins + // to use in the regular and wide display styles. + Rect listItemShadow = new Rect(); + ApiCompatibilityUtils.getDrawable( + mActivity.getResources(), R.drawable.card_middle).getPadding(listItemShadow); + int cardCornerRadius = mActivity.getResources().getDimensionPixelSize( + R.dimen.card_corner_radius); + + assert listItemShadow.left == listItemShadow.right; + // The list item shadow size is used in {@link UiConfig#DISPLAY_STYLE_WIDE} to visually + // align other elements with the edge of the list items. + mListItemLateralShadowSizePx = listItemShadow.left; + // A negative margin is used in {@link UiConfig#DISPLAY_STYLE_REGULAR} to hide the lateral + // shadow. + mDefaultLateralListItemMarginPx = -(listItemShadow.left + cardCornerRadius); + + mSelectableListLayout.setHasWideDisplayStyle(mListItemLateralShadowSizePx); + + // 5. Initialize empty view. mEmptyView = mSelectableListLayout.initializeEmptyView( VectorDrawableCompat.create( mActivity.getResources(), R.drawable.history_big, @@ -105,8 +143,18 @@ mEmptyView.setTextColor(ApiCompatibilityUtils.getColor(mActivity.getResources(), R.color.google_grey_500)); + // 6. Create large icon bridge. + mLargeIconBridge = new LargeIconBridge(Profile.getLastUsedProfile().getOriginalProfile()); + ActivityManager activityManager = ((ActivityManager) ContextUtils + .getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE)); + int maxSize = Math.min((activityManager.getMemoryClass() / 4) * MEGABYTES_TO_BYTES, + FAVICON_MAX_CACHE_SIZE_BYTES); + mLargeIconBridge.createCache(maxSize); + + // 7. Initialize the adapter to load items. mHistoryAdapter.initialize(); + // 8. Add scroll listener to page in more items when necessary. mRecyclerView.addOnScrollListener(new OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { @@ -124,21 +172,22 @@ } }}); - mLargeIconBridge = new LargeIconBridge(Profile.getLastUsedProfile().getOriginalProfile()); - ActivityManager activityManager = ((ActivityManager) ContextUtils - .getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE)); - int maxSize = Math.min((activityManager.getMemoryClass() / 4) * MEGABYTES_TO_BYTES, - FAVICON_MAX_CACHE_SIZE_BYTES); - mLargeIconBridge.createCache(maxSize); - + // 9. Listen to changes in sign in state. SigninManager.get(mActivity).addSignInStateObserver(this); recordUserAction("Show"); } + /** + * @return Whether the history manager UI is displayed in a native page. + */ + public boolean isDisplayedInNativePage() { + return mIsDisplayedInNativePage; + } + @Override public boolean onMenuItemClick(MenuItem item) { - if (item.getItemId() == R.id.close_menu_id && !DeviceFormFactor.isTablet(mActivity)) { + if (item.getItemId() == R.id.close_menu_id && !isDisplayedInNativePage()) { mActivity.finish(); return true; } else if (item.getItemId() == R.id.selection_mode_open_in_new_tab) { @@ -165,6 +214,7 @@ mSelectionDelegate.clearSelection(); return true; } else if (item.getItemId() == R.id.search_menu_id) { + mRecyclerView.setItemAnimator(null); mToolbar.showSearchView(); mToolbarShadow.setVisibility(View.VISIBLE); mSelectableListLayout.setEmptyViewText(R.string.history_manager_no_results); @@ -269,6 +319,7 @@ mSelectableListLayout.setEmptyViewText(R.string.history_manager_empty); mIsSearching = false; setToolbarShadowVisibility(); + mRecyclerView.setItemAnimator(mItemAnimator); } /** @@ -278,6 +329,31 @@ return mLargeIconBridge; } + /** + * @return The SelectableListLayout that displays HistoryItems. + */ + public SelectableListLayout<HistoryItem> getSelectableListLayout() { + return mSelectableListLayout; + } + + /** + * @return The px size of the lateral shadow in the 9-patch used for the list item background. + * This value should be used in {@link UiConfig#DISPLAY_STYLE_REGULAR} to visually align + * elements with the edge of the list items. + */ + public int getListItemLateralShadowSizePx() { + return mListItemLateralShadowSizePx; + } + + /** + * @return The start and end margin for list items when in + * {@link UiConfig#DISPLAY_STYLE_REGULAR}. This value should be used to hide the lateral + * shadows on list items. + */ + public int getDefaultLateralListItemMarginPx() { + return mDefaultLateralListItemMarginPx; + } + private void openItemsInNewTabs(List<HistoryItem> items, boolean isIncognito) { recordSelectionCountHistorgram("Open"); recordUserActionWithOptionalSearch("OpenSelected" + (isIncognito ? "Incognito" : ""));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerToolbar.java index a2c489b..903eedf 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerToolbar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerToolbar.java
@@ -14,7 +14,6 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.preferences.PrefServiceBridge; import org.chromium.chrome.browser.widget.selection.SelectableListToolbar; -import org.chromium.ui.base.DeviceFormFactor; import java.util.List; @@ -36,6 +35,10 @@ */ public void setManager(HistoryManager manager) { mManager = manager; + + if (mManager.isDisplayedInNativePage()) { + getMenu().removeItem(R.id.close_menu_id); + } } @Override @@ -72,10 +75,6 @@ } private void updateMenuItemVisibility() { - if (DeviceFormFactor.isTablet(getContext())) { - getMenu().removeItem(R.id.close_menu_id); - } - // Once the selection mode delete or incognito menu options are removed, they will not // be added back until the user refreshes the history UI. This could happen if the user is // signed in to an account that cannot remove browsing history or has incognito disabled and
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java index 97e328f9..7879df35 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java
@@ -31,7 +31,7 @@ @Override protected void initialize(Activity activity, final Tab tab) { - mHistoryManager = new HistoryManager(activity); + mHistoryManager = new HistoryManager(activity, this); mTitle = activity.getString(R.string.menu_history); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DataReductionPromoInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DataReductionPromoInfoBar.java index 28a373c..cbb9c4f1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DataReductionPromoInfoBar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DataReductionPromoInfoBar.java
@@ -75,7 +75,7 @@ if (DataReductionPromoUtils.getDisplayedInfoBarPromo()) return false; // Only show the promo on HTTP pages. - if (!GURLUtils.getScheme(url).concat("://").equals(UrlConstants.HTTP_SCHEME)) return false; + if (!GURLUtils.getScheme(url).equals(UrlConstants.HTTP_SCHEME)) return false; int currentMilestone = VersionNumberGetter.getMilestoneFromVersionNumber( PrefServiceBridge.getInstance().getAboutVersionStrings().getApplicationVersion());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java index a91dd2f1..5b86b39 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java
@@ -133,7 +133,7 @@ /** Returns true if the snackbar promo is allowed to be shown. */ public boolean isSnackbarPromoAllowed(String url) { - return url.startsWith(UrlConstants.HTTP_SCHEME) && isDataReductionProxyEnabled() + return url.startsWith(UrlConstants.HTTP_URL_PREFIX) && isDataReductionProxyEnabled() && isDataReductionProxyPromoAllowed(); } @@ -279,7 +279,8 @@ } String rewritten = extractUrlFromWebliteQueryParams(url); if (rewritten == null - || !TextUtils.equals(Uri.parse(rewritten).getScheme(), "http")) { + || !TextUtils.equals(Uri.parse(rewritten).getScheme(), + UrlConstants.HTTP_SCHEME)) { return url; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageFactory.java index f7961a2..9996540 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageFactory.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageFactory.java
@@ -23,9 +23,6 @@ * Creates NativePage objects to show chrome-native:// URLs using the native Android view system. */ public class NativePageFactory { - - public static final String CHROME_NATIVE_SCHEME = "chrome-native"; - private static NativePageBuilder sNativePageBuilder = new NativePageBuilder(); @VisibleForTesting @@ -71,7 +68,7 @@ if (url == null) return NativePageType.NONE; Uri uri = Uri.parse(url); - if (!CHROME_NATIVE_SCHEME.equals(uri.getScheme())) { + if (!UrlConstants.CHROME_NATIVE_SCHEME.equals(uri.getScheme())) { return NativePageType.NONE; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java index 502917a..9ac8a25 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java
@@ -290,8 +290,8 @@ // onLoadUrl below covers many exit conditions to stop recording but not all, // such as navigating back. We therefore stop recording if a URL change // indicates some non-Web page was visited. - if (!url.startsWith(UrlConstants.CHROME_SCHEME) - && !url.startsWith(UrlConstants.CHROME_NATIVE_SCHEME)) { + if (!url.startsWith(UrlConstants.CHROME_URL_PREFIX) + && !url.startsWith(UrlConstants.CHROME_NATIVE_URL_PREFIX)) { assert !NewTabPage.isNTPUrl(url); return; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java index e4597ff..926215c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -59,6 +59,7 @@ import org.chromium.chrome.browser.util.MathUtils; import org.chromium.chrome.browser.util.ViewUtils; import org.chromium.chrome.browser.widget.RoundedIconGenerator; +import org.chromium.chrome.browser.widget.displaystyle.UiConfig; import org.chromium.ui.base.DeviceFormFactor; import java.util.Arrays;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ActionItem.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ActionItem.java index 08aa3ba..3e69224 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ActionItem.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ActionItem.java
@@ -10,10 +10,10 @@ import org.chromium.base.VisibleForTesting; import org.chromium.chrome.R; import org.chromium.chrome.browser.ntp.ContextMenuManager; -import org.chromium.chrome.browser.ntp.UiConfig; import org.chromium.chrome.browser.ntp.snippets.CategoryInt; import org.chromium.chrome.browser.suggestions.SuggestionsRanker; import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate; +import org.chromium.chrome.browser.widget.displaystyle.UiConfig; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardViewHolder.java index bc9c9b4..54bf3a4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardViewHolder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardViewHolder.java
@@ -22,9 +22,10 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.ntp.ContextMenuManager; import org.chromium.chrome.browser.ntp.ContextMenuManager.ContextMenuItemId; -import org.chromium.chrome.browser.ntp.UiConfig; import org.chromium.chrome.browser.util.MathUtils; import org.chromium.chrome.browser.util.ViewUtils; +import org.chromium.chrome.browser.widget.displaystyle.MarginResizer; +import org.chromium.chrome.browser.widget.displaystyle.UiConfig; /** * Holder for a generic card. @@ -61,6 +62,7 @@ private final int mCardGap; private final int mDefaultLateralMargin; + private final int mWideLateralMargin; protected final NewTabPageRecyclerView mRecyclerView; @@ -118,15 +120,17 @@ mUiConfig = uiConfig; - mMarginResizer = MarginResizer.createWithViewAdapter(itemView, mUiConfig); - // Configure the resizer to use negative margins on regular display to balance out the // lateral shadow of the card 9-patch and avoid a rounded corner effect. int cardCornerRadius = recyclerView.getResources().getDimensionPixelSize( - R.dimen.snippets_card_corner_radius); + R.dimen.card_corner_radius); assert mCardShadow.left == mCardShadow.right; mDefaultLateralMargin = -(mCardShadow.left + cardCornerRadius); - mMarginResizer.setMargins(mDefaultLateralMargin); + mWideLateralMargin = recyclerView.getResources().getDimensionPixelSize( + R.dimen.ntp_wide_card_lateral_margins); + + mMarginResizer = MarginResizer.createWithViewAdapter(itemView, mUiConfig, + mDefaultLateralMargin, mWideLateralMargin); } @Override @@ -289,7 +293,8 @@ itemView.setPadding(lateralPadding, mMaxPeekPadding, lateralPadding, mMaxPeekPadding); // Adjust the margins. The shadow width is offset via the default lateral margin. - mMarginResizer.setMargins(mDefaultLateralMargin + mMaxPeekPadding - peekPadding); + mMarginResizer.setMargins(mDefaultLateralMargin + mMaxPeekPadding - peekPadding, + mWideLateralMargin); // Set the opacity of the card content to be 0 when peeking and 1 when full width. int itemViewChildCount = ((ViewGroup) itemView).getChildCount();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/MarginResizer.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/MarginResizer.java deleted file mode 100644 index 116e7230..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/MarginResizer.java +++ /dev/null
@@ -1,71 +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. - -package org.chromium.chrome.browser.ntp.cards; - -import android.view.View; -import android.view.ViewGroup.MarginLayoutParams; - -import org.chromium.chrome.R; -import org.chromium.chrome.browser.ntp.DisplayStyleObserver; -import org.chromium.chrome.browser.ntp.UiConfig; - -/** - * Adds lateral margins to the view when the display style is {@link UiConfig#DISPLAY_STYLE_WIDE}. - */ -public class MarginResizer implements DisplayStyleObserver { - private int mDefaultMarginSizePixels; - private final int mWideMarginSizePixels; - private final View mView; - - @UiConfig.DisplayStyle - private int mCurrentDisplayStyle; - - /** - * Factory method that creates a {@link MarginResizer} and wraps it in a - * {@link DisplayStyleObserverAdapter} that will take care of invoking it when appropriate. - * @param view the view that will have its margins resized - * @param config the UiConfig object to subscribe to - * @return the newly created {@link MarginResizer} - */ - public static MarginResizer createWithViewAdapter(View view, UiConfig config) { - MarginResizer marginResizer = new MarginResizer(view); - new DisplayStyleObserverAdapter(view, config, marginResizer); - return marginResizer; - } - - public MarginResizer(View view) { - mView = view; - mWideMarginSizePixels = - view.getResources().getDimensionPixelSize(R.dimen.ntp_wide_card_lateral_margins); - } - - @Override - public void onDisplayStyleChanged(@UiConfig.DisplayStyle int newDisplayStyle) { - mCurrentDisplayStyle = newDisplayStyle; - updateMargins(); - } - - /** - * Sets the lateral margins on the associated view, using the appropriate value depending on - * the current display style. - * @param marginPixels margin size to use in {@link UiConfig#DISPLAY_STYLE_REGULAR}. This value - * will be used as new default value for the lateral margins. - */ - public void setMargins(int marginPixels) { - this.mDefaultMarginSizePixels = marginPixels; - updateMargins(); - } - - private void updateMargins() { - MarginLayoutParams layoutParams = (MarginLayoutParams) mView.getLayoutParams(); - if (mCurrentDisplayStyle == UiConfig.DISPLAY_STYLE_WIDE) { - layoutParams.setMargins(mWideMarginSizePixels, layoutParams.topMargin, - mWideMarginSizePixels, layoutParams.bottomMargin); - } else { - layoutParams.setMargins(mDefaultMarginSizePixels, layoutParams.topMargin, - mDefaultMarginSizePixels, layoutParams.bottomMargin); - } - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java index 311be1c3..80e9851 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
@@ -13,13 +13,13 @@ import org.chromium.base.Callback; import org.chromium.base.VisibleForTesting; import org.chromium.chrome.browser.ntp.ContextMenuManager; -import org.chromium.chrome.browser.ntp.UiConfig; import org.chromium.chrome.browser.ntp.snippets.SectionHeaderViewHolder; import org.chromium.chrome.browser.ntp.snippets.SnippetArticle; import org.chromium.chrome.browser.ntp.snippets.SnippetArticleViewHolder; import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; import org.chromium.chrome.browser.suggestions.PartialUpdateId; import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate; +import org.chromium.chrome.browser.widget.displaystyle.UiConfig; import java.util.List; import java.util.Set;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SignInPromo.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SignInPromo.java index 860248a..ca08ef4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SignInPromo.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SignInPromo.java
@@ -16,7 +16,6 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.ntp.ContextMenuManager; import org.chromium.chrome.browser.ntp.NewTabPage.DestructionObserver; -import org.chromium.chrome.browser.ntp.UiConfig; import org.chromium.chrome.browser.preferences.ChromePreferenceManager; import org.chromium.chrome.browser.signin.AccountSigninActivity; import org.chromium.chrome.browser.signin.SigninAccessPoint; @@ -24,6 +23,7 @@ import org.chromium.chrome.browser.signin.SigninManager.SignInAllowedObserver; import org.chromium.chrome.browser.signin.SigninManager.SignInStateObserver; import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate; +import org.chromium.chrome.browser.widget.displaystyle.UiConfig; /** * Shows a card prompting the user to sign in. This item is also an {@link OptionalLeaf}, and sign
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/StatusCardViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/StatusCardViewHolder.java index fc1be75..811d7bc4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/StatusCardViewHolder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/StatusCardViewHolder.java
@@ -13,7 +13,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.ntp.ContextMenuManager; -import org.chromium.chrome.browser.ntp.UiConfig; +import org.chromium.chrome.browser.widget.displaystyle.UiConfig; /** * ViewHolder for Status and Promo cards.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java index f8beb60..c085f9fd 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
@@ -25,6 +25,7 @@ import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; @@ -38,11 +39,7 @@ public class SuggestionsSection extends InnerNode { private static final String TAG = "NtpCards"; - private static final Set<Integer> SECTION_DISMISSAL_GROUP = new HashSet<>(2); - { - SECTION_DISMISSAL_GROUP.add(1); - SECTION_DISMISSAL_GROUP.add(2); - } + private static final Set<Integer> SECTION_DISMISSAL_GROUP = new HashSet<>(Arrays.asList(1, 2)); private final Delegate mDelegate; private final SuggestionsCategoryInfo mCategoryInfo; @@ -475,6 +472,11 @@ return Collections.emptySet(); } + if (!mMoreButton.isVisible()) { + assert getStartingOffsetForChild(mStatus) == 1; + return Collections.singleton(1); + } + assert SECTION_DISMISSAL_GROUP.contains(getStartingOffsetForChild(mStatus)); assert SECTION_DISMISSAL_GROUP.contains(getStartingOffsetForChild(mMoreButton)); return SECTION_DISMISSAL_GROUP;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SectionHeaderViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SectionHeaderViewHolder.java index f78cce5..4fe489e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SectionHeaderViewHolder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SectionHeaderViewHolder.java
@@ -8,11 +8,11 @@ import android.widget.TextView; import org.chromium.chrome.R; -import org.chromium.chrome.browser.ntp.UiConfig; -import org.chromium.chrome.browser.ntp.cards.MarginResizer; import org.chromium.chrome.browser.ntp.cards.NewTabPageRecyclerView; import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder; import org.chromium.chrome.browser.util.MathUtils; +import org.chromium.chrome.browser.widget.displaystyle.MarginResizer; +import org.chromium.chrome.browser.widget.displaystyle.UiConfig; /** * View holder for the header of a section of cards. @@ -29,7 +29,11 @@ .inflate(R.layout.new_tab_page_snippets_header, recyclerView, false)); mMaxSnippetHeaderHeight = itemView.getResources().getDimensionPixelSize( R.dimen.snippets_article_header_height); - MarginResizer.createWithViewAdapter(itemView, config); + + int wideLateralMargin = recyclerView.getResources().getDimensionPixelSize( + R.dimen.ntp_wide_card_lateral_margins); + MarginResizer.createWithViewAdapter(itemView, config, 0, + wideLateralMargin); } public void onBindViewHolder(SectionHeader header) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java index 76ce767e..6ee4d579 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
@@ -29,15 +29,15 @@ import org.chromium.chrome.browser.favicon.FaviconHelper.IconAvailabilityCallback; import org.chromium.chrome.browser.ntp.ContextMenuManager; import org.chromium.chrome.browser.ntp.ContextMenuManager.ContextMenuItemId; -import org.chromium.chrome.browser.ntp.DisplayStyleObserver; -import org.chromium.chrome.browser.ntp.UiConfig; import org.chromium.chrome.browser.ntp.cards.CardViewHolder; import org.chromium.chrome.browser.ntp.cards.CardsVariationParameters; -import org.chromium.chrome.browser.ntp.cards.DisplayStyleObserverAdapter; import org.chromium.chrome.browser.ntp.cards.ImpressionTracker; import org.chromium.chrome.browser.ntp.cards.NewTabPageRecyclerView; import org.chromium.chrome.browser.ntp.cards.SuggestionsCategoryInfo; import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate; +import org.chromium.chrome.browser.widget.displaystyle.DisplayStyleObserver; +import org.chromium.chrome.browser.widget.displaystyle.DisplayStyleObserverAdapter; +import org.chromium.chrome.browser.widget.displaystyle.UiConfig; import org.chromium.ui.mojom.WindowOpenDisposition; import java.net.URI;
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 8b0f7eb..dc67d574 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
@@ -24,6 +24,7 @@ import org.chromium.base.metrics.RecordUserAction; import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeActivity; +import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.share.ShareHelper; import org.chromium.chrome.browser.snackbar.Snackbar; @@ -148,9 +149,9 @@ public static String stripSchemeFromOnlineUrl(String onlineUrl) { onlineUrl = onlineUrl.trim(); // Offline pages are only saved for https:// and http:// schemes. - if (onlineUrl.startsWith("https://")) { + if (onlineUrl.startsWith(UrlConstants.HTTPS_URL_PREFIX)) { return onlineUrl.substring(8); - } else if (onlineUrl.startsWith("http://")) { + } else if (onlineUrl.startsWith(UrlConstants.HTTP_URL_PREFIX)) { return onlineUrl.substring(7); } else { return onlineUrl;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java index 14db19d6..c42cc3a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
@@ -18,6 +18,7 @@ import org.chromium.chrome.browser.toolbar.Toolbar; import org.chromium.chrome.browser.toolbar.ToolbarActionModeCallback; import org.chromium.chrome.browser.toolbar.ToolbarDataProvider; +import org.chromium.chrome.browser.widget.BottomSheet; import org.chromium.ui.base.WindowAndroid; /** @@ -78,6 +79,12 @@ void setToolbarDataProvider(ToolbarDataProvider model); /** + * Set the bottom sheet for Chrome Home. + * @param sheet The bottom sheet for Chrome Home if it exists. + */ + void setBottomSheet(BottomSheet sheet); + + /** * Sets the menu helper that should be used if there is a menu button in {@link LocationBar}. * @param helper The helper to be used. */
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 40258d2..9620a5d 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
@@ -56,6 +56,7 @@ import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.NativePage; +import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.WindowDelegate; import org.chromium.chrome.browser.appmenu.AppMenuButtonHelper; import org.chromium.chrome.browser.ntp.NativePageFactory; @@ -80,8 +81,8 @@ import org.chromium.chrome.browser.util.FeatureUtilities; import org.chromium.chrome.browser.util.KeyNavigationUtil; import org.chromium.chrome.browser.util.ViewUtils; +import org.chromium.chrome.browser.widget.BottomSheet; import org.chromium.chrome.browser.widget.TintedImageButton; -import org.chromium.chrome.browser.widget.animation.AnimatorProperties; import org.chromium.chrome.browser.widget.animation.CancelAwareAnimatorListener; import org.chromium.components.security_state.ConnectionSecurityLevel; import org.chromium.content_public.browser.LoadUrlParams; @@ -120,7 +121,6 @@ // response (as opposed to treating it like a typed string in the Omnibox). private static final float VOICE_SEARCH_CONFIDENCE_NAVIGATE_THRESHOLD = 0.9f; - private static final int CONTENT_OVERLAY_COLOR = 0xA6000000; private static final int OMNIBOX_RESULTS_BG_COLOR = 0xFFF5F5F6; private static final int OMNIBOX_INCOGNITO_RESULTS_BG_COLOR = 0xFF323232; @@ -133,9 +133,14 @@ * The following additions have been made: "chrome", "ftp". */ private static final HashSet<String> ACCEPTED_SCHEMES = CollectionUtil.newHashSet( - "about", "data", "file", "ftp", "http", "https", "inline", "javascript", "chrome"); + UrlConstants.ABOUT_SCHEME, UrlConstants.DATA_SCHEME, + UrlConstants.FILE_SCHEME, UrlConstants.FTP_SCHEME, + UrlConstants.HTTP_SCHEME, UrlConstants.HTTPS_SCHEME, + UrlConstants.INLINE_SCHEME, UrlConstants.JAVASCRIPT_SCHEME, + UrlConstants.CHROME_SCHEME); private static final HashSet<String> UNSUPPORTED_SCHEMES_TO_SPLIT = - CollectionUtil.newHashSet("file", "javascript", "data"); + CollectionUtil.newHashSet(UrlConstants.FILE_SCHEME, + UrlConstants.JAVASCRIPT_SCHEME, UrlConstants.DATA_SCHEME); protected ImageView mNavigationButton; protected TintedImageButton mSecurityButton; @@ -144,6 +149,9 @@ protected TintedImageButton mMicButton; protected UrlBar mUrlBar; + /** A handle to the bottom sheet for chrome home. */ + private BottomSheet mBottomSheet; + private AutocompleteController mAutocomplete; protected ToolbarDataProvider mToolbarDataProvider; @@ -181,9 +189,10 @@ private Runnable mRequestSuggestions; private ViewGroup mOmniboxResultsContainer; - private ObjectAnimator mFadeInOmniboxBackgroundAnimator; - private ObjectAnimator mFadeOutOmniboxBackgroundAnimator; - private Animator mOmniboxBackgroundAnimator; + private View mFadingView; + private ObjectAnimator mOverlayFadeInAnimator; + private ObjectAnimator mOverlayFadeOutAnimator; + private Animator mOverlayAnimator; private boolean mSuggestionsShown; private boolean mUrlHasFocus; @@ -1041,7 +1050,7 @@ if (mUrlFocusChangeListener != null) mUrlFocusChangeListener.onUrlFocusChange(hasFocus); updateOmniboxResultsContainer(); - if (hasFocus) updateOmniboxResultsContainerBackground(true); + if (hasFocus) updateFadingBackgroundView(true); } /** @@ -1164,6 +1173,11 @@ } @Override + public void setBottomSheet(BottomSheet sheet) { + mBottomSheet = sheet; + } + + @Override public void setMenuButtonHelper(AppMenuButtonHelper helper) { } @Override @@ -1879,10 +1893,10 @@ } else if (v == mMicButton) { RecordUserAction.record("MobileOmniboxVoiceSearch"); startVoiceRecognition(); - } else if (v == mOmniboxResultsContainer) { + } else if (v == mFadingView) { // This will only be triggered when no suggestion items are selected in the container. setUrlBarFocus(false); - updateOmniboxResultsContainerBackground(false); + updateFadingBackgroundView(false); } } @@ -2189,11 +2203,14 @@ private void initOmniboxResultsContainer() { if (mOmniboxResultsContainer != null) return; - ViewStub overlayStub = - (ViewStub) getRootView().findViewById(R.id.omnibox_results_container_stub); + // Use the omnibox results container in the bottom sheet if it exists. + int omniboxResultsContainerId = R.id.omnibox_results_container_stub; + if (mBottomSheet != null) { + omniboxResultsContainerId = R.id.bottom_omnibox_results_container_stub; + } + + ViewStub overlayStub = (ViewStub) getRootView().findViewById(omniboxResultsContainerId); mOmniboxResultsContainer = (ViewGroup) overlayStub.inflate(); - mOmniboxResultsContainer.setBackgroundColor(CONTENT_OVERLAY_COLOR); - mOmniboxResultsContainer.setOnClickListener(this); } private void updateOmniboxResultsContainer() { @@ -2201,7 +2218,7 @@ initOmniboxResultsContainer(); updateOmniboxResultsContainerVisibility(true); } else if (mOmniboxResultsContainer != null) { - updateOmniboxResultsContainerBackground(false); + updateFadingBackgroundView(false); } } @@ -2213,79 +2230,94 @@ if (visible) { mOmniboxResultsContainer.setVisibility(VISIBLE); - if (activity != null) activity.addViewObscuringAllTabs(mOmniboxResultsContainer); + if (activity != null) activity.addViewObscuringAllTabs(mFadingView); } else { mOmniboxResultsContainer.setVisibility(INVISIBLE); - if (activity != null) activity.removeViewObscuringAllTabs(mOmniboxResultsContainer); + if (activity != null) activity.removeViewObscuringAllTabs(mFadingView); } } /** - * Set the background of the omnibox results container. + * Initialize the fading background for when the omnibox is focused. + */ + private void initFadingOverlayView() { + mFadingView = getRootView().findViewById(R.id.fading_focus_target); + mFadingView.setAlpha(0.0f); + mFadingView.setOnClickListener(this); + } + + /** + * Update the fading background view that shows when the omnibox is focused. * @param visible Whether the background should be made visible. */ - private void updateOmniboxResultsContainerBackground(boolean visible) { + private void updateFadingBackgroundView(boolean visible) { if (getToolbarDataProvider() == null) return; + if (mFadingView == null) initFadingOverlayView(); + NewTabPage ntp = getToolbarDataProvider().getNewTabPageForCurrentTab(); boolean locationBarShownInNTP = ntp != null && ntp.isLocationBarShownInNTP(); - if (visible) { - if (locationBarShownInNTP) { - mOmniboxResultsContainer.getBackground().setAlpha(0); - } else { - fadeInOmniboxResultsContainerBackground(); - } + + if (visible && !locationBarShownInNTP) { + // If the location bar is shown in the NTP, the toolbar will eventually trigger a + // fade in. + showFadingOverlay(); } else { - if (locationBarShownInNTP) { - updateOmniboxResultsContainerVisibility(false); - } else { - fadeOutOmniboxResultsContainerBackground(); - } + hideFadingOverlay(!locationBarShownInNTP); } } /** - * Trigger a fade in of the omnibox results background. + * Trigger a fade in of the omnibox results background creating a new animation if necessary. */ - protected void fadeInOmniboxResultsContainerBackground() { - if (mFadeInOmniboxBackgroundAnimator == null) { - mFadeInOmniboxBackgroundAnimator = ObjectAnimator.ofInt( - getRootView().findViewById(R.id.omnibox_results_container).getBackground(), - AnimatorProperties.DRAWABLE_ALPHA_PROPERTY, 0, 255); - mFadeInOmniboxBackgroundAnimator.setDuration(OMNIBOX_CONTAINER_BACKGROUND_FADE_MS); - mFadeInOmniboxBackgroundAnimator.setInterpolator( + protected void showFadingOverlay() { + if (mOverlayFadeInAnimator == null) { + mOverlayFadeInAnimator = ObjectAnimator.ofFloat(mFadingView, ALPHA, 1f); + mOverlayFadeInAnimator.setDuration(OMNIBOX_CONTAINER_BACKGROUND_FADE_MS); + mOverlayFadeInAnimator.setInterpolator( BakedBezierInterpolator.FADE_IN_CURVE); } - runOmniboxResultsFadeAnimation(mFadeInOmniboxBackgroundAnimator); + + mFadingView.setVisibility(View.VISIBLE); + runFadeOverlayAnimation(mOverlayFadeInAnimator); } - private void fadeOutOmniboxResultsContainerBackground() { - if (mFadeOutOmniboxBackgroundAnimator == null) { - mFadeOutOmniboxBackgroundAnimator = ObjectAnimator.ofInt( - getRootView().findViewById(R.id.omnibox_results_container).getBackground(), - AnimatorProperties.DRAWABLE_ALPHA_PROPERTY, 255, 0); - mFadeOutOmniboxBackgroundAnimator.setDuration(OMNIBOX_CONTAINER_BACKGROUND_FADE_MS); - mFadeOutOmniboxBackgroundAnimator.setInterpolator( - BakedBezierInterpolator.FADE_OUT_CURVE); - mFadeOutOmniboxBackgroundAnimator.addListener(new CancelAwareAnimatorListener() { + /** + * Trigger a fade out of the omnibox results background creating a new animation if necessary. + */ + private void hideFadingOverlay(boolean fadeOut) { + if (mOverlayFadeOutAnimator == null) { + mOverlayFadeOutAnimator = ObjectAnimator.ofFloat(mFadingView, ALPHA, 0f); + mOverlayFadeOutAnimator.setDuration(OMNIBOX_CONTAINER_BACKGROUND_FADE_MS); + mOverlayFadeOutAnimator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE); + mOverlayFadeOutAnimator.addListener(new CancelAwareAnimatorListener() { @Override public void onEnd(Animator animator) { - updateOmniboxResultsContainerVisibility(false); + mFadingView.setVisibility(View.GONE); + onFadingOverlayHidden(); } }); } - runOmniboxResultsFadeAnimation(mFadeOutOmniboxBackgroundAnimator); + + runFadeOverlayAnimation(mOverlayFadeOutAnimator); + if (!fadeOut) mOverlayFadeOutAnimator.end(); } - private void runOmniboxResultsFadeAnimation(Animator fadeAnimation) { - if (mOmniboxBackgroundAnimator == fadeAnimation - && mOmniboxBackgroundAnimator.isRunning()) { + /** + * A notification that the fading overlay view is completely hidden. + */ + private void onFadingOverlayHidden() { + updateOmniboxResultsContainerVisibility(false); + } + + private void runFadeOverlayAnimation(Animator fadeAnimation) { + if (mOverlayAnimator == fadeAnimation && mOverlayAnimator.isRunning()) { return; - } else if (mOmniboxBackgroundAnimator != null) { - mOmniboxBackgroundAnimator.cancel(); + } else if (mOverlayAnimator != null) { + mOverlayAnimator.cancel(); } - mOmniboxBackgroundAnimator = fadeAnimation; - mOmniboxBackgroundAnimator.start(); + mOverlayAnimator = fadeAnimation; + mOverlayAnimator.start(); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java index 0e63c75..5006857 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
@@ -261,7 +261,7 @@ NewTabPage ntp = getToolbarDataProvider().getNewTabPageForCurrentTab(); if (hasFocus && ntp != null && ntp.isLocationBarShownInNTP()) { - fadeInOmniboxResultsContainerBackground(); + showFadingOverlay(); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java index 9cc09d6c..2fbe518 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java
@@ -23,6 +23,7 @@ import org.chromium.base.annotations.SuppressFBWarnings; import org.chromium.base.metrics.RecordHistogram; import org.chromium.chrome.browser.ChromeFeatureList; +import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.preferences.website.ContentSetting; import org.chromium.chrome.browser.preferences.website.GeolocationInfo; import org.chromium.chrome.browser.preferences.website.WebsitePreferenceBridge; @@ -182,8 +183,6 @@ /** The maximum age in milliseconds of a location before we'll request a refresh. */ private static final int REFRESH_LOCATION_AGE = 5 * 60 * 1000; // 5 minutes - private static final String HTTPS_SCHEME = "https"; - /** The time of the first location refresh. Contains Long.MAX_VALUE if not set. */ private static long sFirstLocationTime = Long.MAX_VALUE; @@ -220,7 +219,7 @@ if (!UrlUtilities.nativeIsGoogleSearchUrl(url)) return UNSUITABLE_URL; Uri uri = Uri.parse(url); - if (!HTTPS_SCHEME.equals(uri.getScheme())) return NOT_HTTPS; + if (!UrlConstants.HTTPS_SCHEME.equals(uri.getScheme())) return NOT_HTTPS; if (!hasGeolocationPermission()) { if (recordUma) recordHistogram(UMA_LOCATION_DISABLED_FOR_CHROME_APP);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFactory.java index ca17fba..335d160 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFactory.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFactory.java
@@ -13,6 +13,7 @@ import android.util.Pair; import org.chromium.base.ContextUtils; +import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppCreatedCallback; import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppFactoryAddition; import org.chromium.content_public.browser.WebContents; @@ -27,9 +28,6 @@ private static final String ACTION_IS_READY_TO_PAY = "org.chromium.intent.action.IS_READY_TO_PAY"; - /** Prefix of all method names that can be constructed using this factory. */ - static final String METHOD_PREFIX = "https://"; - /** The action name for the Pay Basic-card Intent. */ private static final String ACTION_PAY_BASIC_CARD = "org.chromium.intent.action.PAY_BASIC_CARD"; @@ -47,7 +45,7 @@ Intent payIntent = new Intent(); for (String methodName : methods) { - if (methodName.startsWith(METHOD_PREFIX)) { + if (methodName.startsWith(UrlConstants.HTTPS_URL_PREFIX)) { payIntent.setAction(AndroidPaymentApp.ACTION_PAY); payIntent.setData(Uri.parse(methodName)); } else if (methodName.equals(BASIC_CARD_PAYMENT_METHOD)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java index 8d2e2630..a9eceba 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -18,6 +18,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeFeatureList; +import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.autofill.PersonalDataManager; import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile; import org.chromium.chrome.browser.favicon.FaviconHelper; @@ -410,7 +411,7 @@ // the payment request UI, thus can't be skipped. && mMethodData.keySet().iterator().next() != null && mMethodData.keySet().iterator().next().startsWith( - AndroidPaymentAppFactory.METHOD_PREFIX); + UrlConstants.HTTPS_URL_PREFIX); List<AutofillProfile> profiles = null; if (requestShipping || requestPayerName || requestPayerPhone || requestPayerEmail) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/BillingAddressAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/BillingAddressAdapter.java index be4797f..927c3f6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/BillingAddressAdapter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/BillingAddressAdapter.java
@@ -6,24 +6,21 @@ import android.content.Context; import android.content.res.Resources; -import android.graphics.Rect; import android.graphics.Typeface; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; import android.widget.TextView; import org.chromium.base.ApiCompatibilityUtils; import org.chromium.chrome.R; import org.chromium.chrome.browser.widget.TintedDrawable; -import java.util.ArrayList; import java.util.List; /** - * Subclass of ArrayAdapter used to display a dropdown hint that won't appear in the expanded - * dropdown options but will be used when no element is selected. The last shown element will have a - * "+" icon on its left and a blue tint to indicate the option to add an element. + * Subclass of DropdownFieldAdapter used to display a dropdown hint that won't appear in the + * expanded dropdown options but will be used when no element is selected. The last shown element + * will have a "+" icon on its left and a blue tint to indicate the option to add an element. * * @param <T> The type of element to be inserted into the adapter. * @@ -38,7 +35,7 @@ * . hint . -> hidden * .............. */ -public class BillingAddressAdapter<T> extends ArrayAdapter<T> { +public class BillingAddressAdapter<T> extends DropdownFieldAdapter<T> { private final int mTextViewResourceId; /** @@ -59,7 +56,7 @@ public BillingAddressAdapter( Context context, int resource, int textViewResourceId, List<T> objects, T hint) { // Make a copy of objects so the hint is not added to the original list. - super(context, resource, textViewResourceId, new ArrayList<T>(objects)); + super(context, resource, textViewResourceId, objects); // The hint is added as the last element. It will not be shown when the dropdown is // expanded and not be taken into account in the getCount function. add(hint); @@ -75,19 +72,6 @@ } @Override - public View getView(int position, View convertView, ViewGroup parent) { - View view = super.getView(position, convertView, parent); - - // Add the left and right padding of the parent's background to the selected item view to - // avoid overlaping the downward triangle. - Rect rect = new Rect(); - parent.getBackground().getPadding(rect); - view.setPadding(view.getPaddingLeft() + rect.left, view.getPaddingTop(), - view.getPaddingRight() + rect.right, view.getPaddingBottom()); - return view; - } - - @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { TextView textView = convertView == null ? null
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DropdownFieldAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DropdownFieldAdapter.java new file mode 100644 index 0000000..b8984db --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DropdownFieldAdapter.java
@@ -0,0 +1,60 @@ +// 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. + +package org.chromium.chrome.browser.payments.ui; + +import android.content.Context; +import android.graphics.Rect; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; + +import java.util.ArrayList; +import java.util.List; + +/** + * Dropdown field adapter with proper padding for the selected item view. + * + * @param <T> The type of element to be inserted into the adapter. + */ +public class DropdownFieldAdapter<T> extends ArrayAdapter<T> { + /** + * Creates an array adapter. + * + * @param context The current context. + * @param resource The resource ID for a layout file containing a layout to use when + * instantiating views. + * @param textViewResourceId The id of the TextView within the layout resource to be populated. + * @param objects The objects to represent in the ListView. + */ + public DropdownFieldAdapter( + Context context, int resource, int textViewResourceId, List<T> objects) { + super(context, resource, textViewResourceId, new ArrayList<T>(objects)); + } + + /** + * Creates an array adapter. + * + * @param context The current context. + * @param resource The resource ID for a layout file containing a layout to use when + * instantiating views. + * @param objects The objects to represent in the ListView. + */ + public DropdownFieldAdapter(Context context, int resource, List<T> objects) { + super(context, resource, new ArrayList<T>(objects)); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view = super.getView(position, convertView, parent); + + // Add the left and right padding of the parent's background to the selected item view to + // avoid overlaping the downward triangle. + Rect rect = new Rect(); + parent.getBackground().getPadding(rect); + view.setPadding(view.getPaddingLeft() + rect.left, view.getPaddingTop(), + view.getPaddingRight() + rect.right, view.getPaddingBottom()); + return view; + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorDropdownField.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorDropdownField.java index 4501767..ed7d25935 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorDropdownField.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorDropdownField.java
@@ -76,7 +76,7 @@ // ommited in the count. if (mFieldModel.getValue() == null) mSelectedIndex = adapter.getCount(); } else { - adapter = new ArrayAdapter<CharSequence>( + adapter = new DropdownFieldAdapter<CharSequence>( context, R.layout.multiline_spinner_item, dropdownValues); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/HttpRequest.java b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/HttpRequest.java index 11feaebc..c306a70 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/HttpRequest.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/HttpRequest.java
@@ -5,6 +5,8 @@ import org.chromium.base.ThreadUtils; +import org.chromium.chrome.browser.UrlConstants; + import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; @@ -42,7 +44,8 @@ mUrl = new URL(url); mUserAgent = userAgent; mAcceptLanguage = acceptLanguage; - if (!mUrl.getProtocol().equals("http") && !mUrl.getProtocol().equals("https")) { + if (!mUrl.getProtocol().equals(UrlConstants.HTTP_SCHEME) + && !mUrl.getProtocol().equals(UrlConstants.HTTPS_SCHEME)) { throw new MalformedURLException("This is not a http or https URL: " + url); } mCallback = callback;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfileEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfileEditor.java index 2cf26a8..b2435d8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfileEditor.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfileEditor.java
@@ -19,6 +19,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.autofill.PersonalDataManager; import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile; +import org.chromium.chrome.browser.payments.ui.DropdownFieldAdapter; import org.chromium.chrome.browser.preferences.autofill.AutofillProfileBridge.AddressField; import org.chromium.chrome.browser.preferences.autofill.AutofillProfileBridge.AddressUiComponent; import org.chromium.chrome.browser.preferences.autofill.AutofillProfileBridge.DropdownKeyValue; @@ -124,8 +125,9 @@ mCountryCodes.add(country.getKey()); } - ArrayAdapter<DropdownKeyValue> countriesAdapter = new ArrayAdapter<DropdownKeyValue>( - getActivity(), android.R.layout.simple_spinner_item, countries); + ArrayAdapter<DropdownKeyValue> countriesAdapter = + new DropdownFieldAdapter<DropdownKeyValue>( + getActivity(), android.R.layout.simple_spinner_item, countries); countriesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mCountriesDropdown.setAdapter(countriesAdapter); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProvider.java index f683598..ad25a28 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProvider.java
@@ -33,6 +33,7 @@ import org.chromium.base.annotations.SuppressFBWarnings; import org.chromium.base.library_loader.LibraryProcessType; import org.chromium.base.library_loader.ProcessInitException; +import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.database.SQLiteCursor; import org.chromium.chrome.browser.externalauth.ExternalAuthUtils; import org.chromium.chrome.browser.init.ChromeBrowserInitializer; @@ -293,15 +294,16 @@ boolean matchTitles = false; Vector<String> args = new Vector<String>(); String like = selectionArgs[0] + "%"; - if (selectionArgs[0].startsWith("http") || selectionArgs[0].startsWith("file")) { + if (selectionArgs[0].startsWith(UrlConstants.HTTP_SCHEME) + || selectionArgs[0].startsWith(UrlConstants.FILE_SCHEME)) { args.add(like); } else { // Match against common URL prefixes. - args.add("http://" + like); - args.add("https://" + like); - args.add("http://www." + like); - args.add("https://www." + like); - args.add("file://" + like); + args.add(UrlConstants.HTTP_URL_PREFIX + like); + args.add(UrlConstants.HTTPS_URL_PREFIX + like); + args.add(UrlConstants.HTTP_URL_PREFIX + "www." + like); + args.add(UrlConstants.HTTPS_URL_PREFIX + "www." + like); + args.add(UrlConstants.FILE_URL_PREFIX + like); matchTitles = true; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetContent.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetContent.java index 07dfcb1..64705be 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetContent.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetContent.java
@@ -6,11 +6,11 @@ import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; +import android.view.View; import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ntp.ContextMenuManager; -import org.chromium.chrome.browser.ntp.UiConfig; import org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter; import org.chromium.chrome.browser.ntp.cards.NewTabPageRecyclerView; import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge; @@ -18,6 +18,8 @@ import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tabmodel.TabModelSelector; +import org.chromium.chrome.browser.widget.BottomSheet; +import org.chromium.chrome.browser.widget.displaystyle.UiConfig; /** * Provides content to be displayed inside of the Home tab of bottom sheet. @@ -25,7 +27,7 @@ * TODO(dgn): If the bottom sheet view is not recreated across tab changes, it will have to be * notified of it, at least when it is pulled up on the new tab. */ -public class SuggestionsBottomSheetContent { +public class SuggestionsBottomSheetContent implements BottomSheet.BottomSheetContent { private final NewTabPageRecyclerView mRecyclerView; private final ContextMenuManager mContextMenuManager; private final SuggestionsUiDelegateImpl mSuggestionsManager; @@ -53,10 +55,16 @@ mRecyclerView.setUpSwipeToDismiss(); } + @Override public RecyclerView getScrollingContentView() { return mRecyclerView; } + @Override + public View getToolbarView() { + return null; + } + public ContextMenuManager getContextMenuManager() { return mContextMenuManager; }
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 4a6b0e36..d964c7d 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
@@ -36,6 +36,8 @@ public class SuggestionsNavigationDelegateImpl implements SuggestionsNavigationDelegate { private static final String CHROME_CONTENT_SUGGESTIONS_REFERRER = "https://www.googleapis.com/auth/chrome-content-suggestions"; + private static final String NEW_TAB_URL_HELP = + "https://support.google.com/chrome/?p=new_tab"; private final Activity mActivity; private final Profile mProfile; @@ -82,10 +84,9 @@ @Override public void navigateToHelpPage() { NewTabPageUma.recordAction(NewTabPageUma.ACTION_CLICKED_LEARN_MORE); - String url = "https://support.google.com/chrome/?p=new_tab"; // TODO(mastiz): Change this to LINK? openUrl(WindowOpenDisposition.CURRENT_TAB, - new LoadUrlParams(url, PageTransition.AUTO_BOOKMARK)); + new LoadUrlParams(NEW_TAB_URL_HELP, PageTransition.AUTO_BOOKMARK)); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsUiDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsUiDelegateImpl.java index 74490ce..50bcd29 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsUiDelegateImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsUiDelegateImpl.java
@@ -12,6 +12,7 @@ import org.chromium.base.ThreadUtils; import org.chromium.base.metrics.RecordHistogram; import org.chromium.chrome.browser.ChromeFeatureList; +import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.favicon.FaviconHelper; import org.chromium.chrome.browser.favicon.FaviconHelper.FaviconImageCallback; import org.chromium.chrome.browser.favicon.FaviconHelper.IconAvailabilityCallback; @@ -192,6 +193,6 @@ } private boolean isLocalUrl(String url) { - return "file".equals(Uri.parse(url).getScheme()); + return UrlConstants.FILE_SCHEME.equals(Uri.parse(url).getScheme()); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java index 0d26479..9dbaa3f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
@@ -12,6 +12,7 @@ import org.chromium.base.metrics.RecordUserAction; import org.chromium.chrome.browser.IntentHandler; +import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.contextmenu.ContextMenuItemDelegate; import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings; @@ -238,7 +239,8 @@ */ private boolean isSpdyProxyEnabledForUrl(String url) { if (DataReductionProxySettings.getInstance().isDataReductionProxyEnabled() - && url != null && !url.toLowerCase(Locale.US).startsWith("https://") + && url != null && !url.toLowerCase(Locale.US).startsWith( + UrlConstants.HTTPS_URL_PREFIX) && !isIncognito()) { return true; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java index 344a4d8..8b70115 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
@@ -133,8 +133,8 @@ String url = mTab.getUrl(); boolean enableHidingBrowserControls = url != null; - enableHidingBrowserControls &= !url.startsWith(UrlConstants.CHROME_SCHEME); - enableHidingBrowserControls &= !url.startsWith(UrlConstants.CHROME_NATIVE_SCHEME); + enableHidingBrowserControls &= !url.startsWith(UrlConstants.CHROME_URL_PREFIX); + enableHidingBrowserControls &= !url.startsWith(UrlConstants.CHROME_NATIVE_URL_PREFIX); int securityState = mTab.getSecurityLevel(); enableHidingBrowserControls &= (securityState != ConnectionSecurityLevel.DANGEROUS
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java index c5cdcf89..82d3f434 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java
@@ -51,5 +51,13 @@ @Override public void setBottomSheet(BottomSheet sheet) { mBottomSheet = sheet; + getLocationBar().setBottomSheet(mBottomSheet); + } + + @Override + public boolean shouldIgnoreSwipeGesture() { + // Only detect swipes if the bottom sheet in the peeking state and not animating. + return mBottomSheet.getSheetState() != BottomSheet.SHEET_STATE_PEEK + || mBottomSheet.isRunningSettleAnimation() || super.shouldIgnoreSwipeGesture(); } }
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 1b3af14..ae93dacf 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
@@ -321,7 +321,7 @@ // always return the url. We postpone the title animation until the title is authentic. if ((mState == STATE_DOMAIN_AND_TITLE || mState == STATE_TITLE_ONLY) && !title.equals(currentTab.getUrl()) - && !title.equals(UrlConstants.ABOUT_BLANK)) { + && !title.equals(UrlConstants.ABOUT_BLANK_DISPLAY_URL)) { // Delay the title animation until security icon animation finishes. ThreadUtils.postOnUiThreadDelayed(mTitleAnimationStarter, TITLE_ANIM_DELAY_MS); } @@ -357,7 +357,7 @@ // If we have taken a pre-initialized WebContents, then the starting URL // is "about:blank". We should not display it. if (NativePageFactory.isNativePageUrl(url, getCurrentTab().isIncognito()) - || UrlConstants.ABOUT_BLANK.equals(url)) { + || UrlConstants.ABOUT_BLANK_DISPLAY_URL.equals(url)) { mUrlBar.setUrl("", null); return; }
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 1b771ee..73e32d0 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
@@ -201,7 +201,7 @@ mToolbar = (ToolbarLayout) controlContainer.findViewById(R.id.toolbar); mToolbar.setPaintInvalidator(invalidator); - mToolbar.setBottomSheet(activity.getBottomSheet()); + if (activity.getBottomSheet() != null) mToolbar.setBottomSheet(activity.getBottomSheet()); mActionModeController = new ActionModeController(activity, mActionBarDelegate); mActionModeController.setCustomSelectionActionModeCallback(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/UrlUtilities.java b/chrome/android/java/src/org/chromium/chrome/browser/util/UrlUtilities.java index 4b2caa8..dcd355a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/util/UrlUtilities.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/util/UrlUtilities.java
@@ -10,6 +10,8 @@ import org.chromium.base.Log; import org.chromium.base.VisibleForTesting; +import org.chromium.chrome.browser.UrlConstants; + import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; @@ -30,7 +32,9 @@ * URI schemes that are internal to Chrome. */ private static final HashSet<String> INTERNAL_SCHEMES = CollectionUtil.newHashSet( - "chrome", "chrome-native", "about"); + UrlConstants.CHROME_SCHEME, + UrlConstants.CHROME_NATIVE_SCHEME, + UrlConstants.ABOUT_SCHEME); // Patterns used in validateIntentUrl. private static final Pattern DNS_HOSTNAME_PATTERN = @@ -42,7 +46,7 @@ private static final Pattern URL_SCHEME_PATTERN = Pattern.compile("^[a-zA-Z]+$"); - private static final String TEL_SCHEME = "tel:"; + private static final String TEL_URL_PREFIX = "tel:"; /** * @param uri A URI. @@ -50,7 +54,7 @@ * @return True if the URI's scheme is phone number scheme. */ public static boolean isTelScheme(String uri) { - return uri != null && uri.startsWith(TEL_SCHEME); + return uri != null && uri.startsWith(TEL_URL_PREFIX); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java index 0d9f110..e32b4bb 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java
@@ -11,21 +11,18 @@ import android.graphics.Region; import android.support.annotation.IntDef; import android.support.annotation.Nullable; -import android.support.v4.view.ScrollingView; +import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; -import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.widget.FrameLayout; -import android.widget.LinearLayout; import org.chromium.chrome.R; -import org.chromium.chrome.browser.NativePage; -import org.chromium.chrome.browser.ntp.NewTabPage; +import org.chromium.chrome.browser.suggestions.SuggestionsBottomSheetContent; import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.util.MathUtils; @@ -42,7 +39,7 @@ * All the computation in this file is based off of the bottom of the screen instead of the top * for simplicity. This means that the bottom of the screen is 0 on the Y axis. */ -public class BottomSheet extends LinearLayout { +public class BottomSheet extends FrameLayout { /** The different states that the bottom sheet can have. */ @IntDef({SHEET_STATE_PEEK, SHEET_STATE_HALF, SHEET_STATE_FULL}) @Retention(RetentionPolicy.SOURCE) @@ -107,17 +104,40 @@ /** Used for getting the current tab. */ private TabModelSelector mTabModelSelector; - /** A handle to the native page being shown by the sheet. */ - private NativePage mNativePage; + /** A handle to the content being shown by the sheet. */ + private BottomSheetContent mSheetContent; + + /** This is the default {@link BottomSheetContent} to show when the bottom sheet is opened. */ + private BottomSheetContent mSuggestionsContent; /** A handle to the toolbar control container. */ private View mControlContainer; /** A handle to the FrameLayout that holds the content of the bottom sheet. */ - private FrameLayout mBottomSheetContent; + private FrameLayout mBottomSheetContentContainer; - /** A handle to the main scrolling view in the bottom sheet's content. */ - private ScrollingView mScrollingContentView; + /** + * An interface defining content that can be displayed inside of the bottom sheet for Chrome + * Home. + */ + public interface BottomSheetContent { + /** + * Gets the {@link ScrollingView} that holds the content to be displayed in the Chrome Home + * bottom sheet. + * @return The scrolling content view. + */ + RecyclerView getScrollingContentView(); + + /** + * Get the {@link View} that contains the toolbar specific to the content being displayed. + * If null is returned, the omnibox is used. + * TODO(mdjones): This still needs implementation in the sheet. + * + * @return The toolbar view. + */ + @Nullable + View getToolbarView(); + } /** * This class is responsible for detecting swipe and scroll events on the bottom sheet or @@ -142,7 +162,7 @@ // Cancel the settling animation if it is running so it doesn't conflict with where the // user wants to move the sheet. - boolean wasSettleAnimatorRunning = mSettleAnimator != null; + boolean wasSettleAnimatorRunning = isRunningSettleAnimation(); cancelAnimation(); mVelocityTracker.addMovement(e2); @@ -153,9 +173,12 @@ MathUtils.areFloatsEqual(currentShownRatio, mStateRatios[mStateRatios.length - 1]); + RecyclerView scrollingView = null; + if (mSheetContent != null) scrollingView = mSheetContent.getScrollingContentView(); + // Allow the bottom sheet's content to be scrolled up without dragging the sheet down. - if (!isTouchEventInToolbar(e2) && isSheetInMaxPosition && mScrollingContentView != null - && mScrollingContentView.computeVerticalScrollOffset() > 0) { + if (!isTouchEventInToolbar(e2) && isSheetInMaxPosition && scrollingView != null + && scrollingView.computeVerticalScrollOffset() > 0) { mIsScrolling = false; return false; } @@ -211,7 +234,6 @@ public BottomSheet(Context context, AttributeSet atts) { super(context, atts); - setOrientation(LinearLayout.VERTICAL); mVelocityTracker = VelocityTracker.obtain(); mGestureDetector = new GestureDetector(context, new BottomSheetSwipeDetector()); @@ -282,7 +304,7 @@ mControlContainer = controlContainer; mToolbarHeight = mControlContainer.getHeight(); - mBottomSheetContent = (FrameLayout) findViewById(R.id.bottom_sheet_content); + mBottomSheetContentContainer = (FrameLayout) findViewById(R.id.bottom_sheet_content); mCurrentState = SHEET_STATE_PEEK; @@ -340,24 +362,32 @@ * A notification that the sheet is exiting the peek state into one that shows content. */ private void onExitPeekState() { - if (mNativePage == null) { - showNativePage(new NewTabPage(mTabModelSelector.getCurrentTab().getActivity(), - mTabModelSelector.getCurrentTab(), mTabModelSelector)); + if (mSuggestionsContent == null) { + mSuggestionsContent = new SuggestionsBottomSheetContent( + mTabModelSelector.getCurrentTab().getActivity(), + mTabModelSelector.getCurrentTab(), mTabModelSelector); } + + showContent(mSuggestionsContent); } /** - * Show a native page in the bottom sheet's content area. - * @param page The NativePage to show. + * Show content in the bottom sheet's content area. + * @param page The BottomSheetContent to show. */ - private void showNativePage(NativePage page) { - if (mNativePage != null) mBottomSheetContent.removeView(mNativePage.getView()); + private void showContent(BottomSheetContent content) { + // If the desired content is already showing, do nothing. + if (mSheetContent == content) return; - mNativePage = page; - mBottomSheetContent.addView(mNativePage.getView()); - mScrollingContentView = findScrollingChild(mNativePage.getView()); + if (mSheetContent != null) { + mBottomSheetContentContainer.removeView(mSheetContent.getScrollingContentView()); + mSheetContent = null; + } - mNativePage.updateForUrl(""); + if (content == null) return; + + mSheetContent = content; + mBottomSheetContentContainer.addView(mSheetContent.getScrollingContentView()); } /** @@ -385,31 +415,16 @@ // Compute the height that the content section of the bottom sheet. float contentHeight = (mContainerHeight * mStateRatios[mStateRatios.length - 1]) - mToolbarHeight; - mBottomSheetContent.setLayoutParams( - new LinearLayout.LayoutParams((int) mContainerWidth, (int) contentHeight)); - } - /** - * Find the first ScrollingView in a view hierarchy. - * TODO(mdjones): The root of native pages should be a ScrollingView so this logic is not - * necessary. - * @param view The root of the tree or subtree. - * @return The first scrolling view or null. - */ - private ScrollingView findScrollingChild(@Nullable View view) { - if (view instanceof ScrollingView) { - return (ScrollingView) view; - } - if (view instanceof ViewGroup) { - ViewGroup group = (ViewGroup) view; - for (int i = 0, count = group.getChildCount(); i < count; i++) { - ScrollingView scrollingChild = findScrollingChild(group.getChildAt(i)); - if (scrollingChild != null) { - return scrollingChild; - } - } - } - return null; + MarginLayoutParams sheetContentParams = + (MarginLayoutParams) mBottomSheetContentContainer.getLayoutParams(); + sheetContentParams.width = (int) mContainerWidth; + sheetContentParams.height = (int) contentHeight; + sheetContentParams.topMargin = (int) mToolbarHeight; + + MarginLayoutParams toolbarShadowParams = + (MarginLayoutParams) findViewById(R.id.toolbar_shadow).getLayoutParams(); + toolbarShadowParams.topMargin = (int) mToolbarHeight; } /** @@ -515,7 +530,7 @@ * If the animation to settle the sheet in one of its states is running. * @return True if the animation is running. */ - private boolean isRunningSettleAnimation() { + public boolean isRunningSettleAnimation() { return mSettleAnimator != null; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/DateDividedAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/DateDividedAdapter.java index ee707031..da379763 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/DateDividedAdapter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/DateDividedAdapter.java
@@ -49,6 +49,9 @@ /** Position of the TimedItem in the list, or {@link #INVALID_POSITION} if not shown. */ private int mPosition = INVALID_POSITION; + private boolean mIsFirstInGroup; + private boolean mIsLastInGroup; + /** See {@link #mPosition}. */ private final void setPosition(int position) { mPosition = position; @@ -59,6 +62,34 @@ return mPosition; } + /** + * @param isFirst Whether this item is the first in its group. + */ + public final void setIsFirstInGroup(boolean isFirst) { + mIsFirstInGroup = isFirst; + } + + /** + * @param isLast Whether this item is the last in its group. + */ + public final void setIsLastInGroup(boolean isLast) { + mIsLastInGroup = isLast; + } + + /** + * @return Whether this item is the first in its group. + */ + public boolean isFirstInGroup() { + return mIsFirstInGroup; + } + + /** + * @return Whether this item is the last in its group. + */ + public boolean isLastInGroup() { + return mIsLastInGroup; + } + /** @return The timestamp for this item. */ public abstract long getTimestamp(); @@ -71,7 +102,10 @@ public abstract long getStableId(); } - private static class DateViewHolder extends RecyclerView.ViewHolder { + /** + * A {@link RecyclerView.ViewHolder} that displays a date header. + */ + public static class DateViewHolder extends RecyclerView.ViewHolder { private TextView mTextView; public DateViewHolder(View view) { @@ -79,6 +113,9 @@ if (view instanceof TextView) mTextView = (TextView) view; } + /** + * @param date The date that this DateViewHolder should display. + */ public void setDate(Date date) { // Calender.getInstance() may take long time to run, so Calendar object should be reused // as much as possible. @@ -145,9 +182,12 @@ mIndex = index; sortIfNeeded(); - for (TimedItem item : mItems) { + for (int i = 0; i < mItems.size(); i++) { index += 1; + TimedItem item = mItems.get(i); item.setPosition(index); + item.setIsFirstInGroup(i == 0); + item.setIsLastInGroup(i == mItems.size() - 1); } } @@ -244,13 +284,22 @@ /** * Creates a {@link BasicViewHolder} in the given view parent for the footer. - * @see #onCreateViewHolder(ViewGroup, int) + * See {@link #onCreateViewHolder(ViewGroup, int)}. */ protected BasicViewHolder createFooter(ViewGroup parent) { return null; } /** + * Creates a {@link DateViewHolder} in the given view parent. + * @see #onCreateViewHolder(ViewGroup, int) + */ + protected DateViewHolder createDateViewHolder(ViewGroup parent) { + return new DateViewHolder(LayoutInflater.from(parent.getContext()).inflate( + getTimedItemViewResId(), parent, false)); + } + + /** * Binds the {@link ViewHolder} with the given {@link TimedItem}. * @see #onBindViewHolder(ViewHolder, int) */ @@ -415,8 +464,7 @@ @Override public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == TYPE_DATE) { - return new DateViewHolder(LayoutInflater.from(parent.getContext()).inflate( - getTimedItemViewResId(), parent, false)); + return createDateViewHolder(parent); } else if (viewType == TYPE_NORMAL) { return createViewHolder(parent); } else if (viewType == TYPE_HEADER) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/RoundedIconGenerator.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/RoundedIconGenerator.java index e57b4076..7b195ac 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/RoundedIconGenerator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/RoundedIconGenerator.java
@@ -172,9 +172,9 @@ if (!TextUtils.isEmpty(domain)) return domain; // Special-case chrome:// and chrome-native:// URLs. - if (url.startsWith(UrlConstants.CHROME_SCHEME) - || url.startsWith(UrlConstants.CHROME_NATIVE_SCHEME)) { - return "chrome"; + if (url.startsWith(UrlConstants.CHROME_URL_PREFIX) + || url.startsWith(UrlConstants.CHROME_NATIVE_URL_PREFIX)) { + return UrlConstants.CHROME_SCHEME; } // Use the host component of |url| when it can be parsed as a URI.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/DisplayStyleObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/DisplayStyleObserver.java similarity index 74% rename from chrome/android/java/src/org/chromium/chrome/browser/ntp/DisplayStyleObserver.java rename to chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/DisplayStyleObserver.java index 76d6ac6d..5b6565c1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/DisplayStyleObserver.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/DisplayStyleObserver.java
@@ -2,14 +2,14 @@ // 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.ntp; +package org.chromium.chrome.browser.widget.displaystyle; /** * Gets notified of changes in the display style. * * @see UiConfig.DisplayStyle * @see UiConfig#getCurrentDisplayStyle() - * @see org.chromium.chrome.browser.ntp.cards.DisplayStyleObserverAdapter + * @see org.chromium.chrome.browser.widget.displaystyle.DisplayStyleObserverAdapter */ public interface DisplayStyleObserver { void onDisplayStyleChanged(@UiConfig.DisplayStyle int newDisplayStyle);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/DisplayStyleObserverAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/DisplayStyleObserverAdapter.java similarity index 93% rename from chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/DisplayStyleObserverAdapter.java rename to chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/DisplayStyleObserverAdapter.java index 1db900b0..17f8820 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/DisplayStyleObserverAdapter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/DisplayStyleObserverAdapter.java
@@ -2,13 +2,10 @@ // 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.ntp.cards; +package org.chromium.chrome.browser.widget.displaystyle; import android.view.View; -import org.chromium.chrome.browser.ntp.DisplayStyleObserver; -import org.chromium.chrome.browser.ntp.UiConfig; - /** * Implementation of {@link DisplayStyleObserver} designed to play nicely with * {@link android.support.v7.widget.RecyclerView}. It will not notify of changes when the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/MarginResizer.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/MarginResizer.java new file mode 100644 index 0000000..4dfe5f2 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/MarginResizer.java
@@ -0,0 +1,74 @@ +// 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. + +package org.chromium.chrome.browser.widget.displaystyle; + +import android.view.View; +import android.view.ViewGroup.MarginLayoutParams; + +/** + * Changes a view's margins when switching between {@link UiConfig} display styles. + */ +public class MarginResizer implements DisplayStyleObserver { + /** The default value for the lateral margins. */ + private int mDefaultMarginSizePixels; + /** The wide display value for the lateral margins. */ + private int mWideMarginSizePixels; + private final View mView; + + @UiConfig.DisplayStyle + private int mCurrentDisplayStyle; + + /** + * Factory method that creates a {@link MarginResizer} and wraps it in a + * {@link DisplayStyleObserverAdapter} that will take care of invoking it when appropriate. + * @param view The view that will have its margins resized. + * @param config The UiConfig object to subscribe to. + * @param defaultMarginPixels Margin size to use in {@link UiConfig#DISPLAY_STYLE_REGULAR}. + * @param wideMarginPixels Margin size to use in {@link UiConfig#DISPLAY_STYLE_WIDE}. + * @return The newly created {@link MarginResizer}. + */ + public static MarginResizer createWithViewAdapter(View view, UiConfig config, + int defaultMarginPixels, int wideMarginPixels) { + MarginResizer marginResizer = + new MarginResizer(view, defaultMarginPixels, wideMarginPixels); + new DisplayStyleObserverAdapter(view, config, marginResizer); + return marginResizer; + } + + public MarginResizer(View view, int defaultMarginPixels, int wideMarginPixels) { + mView = view; + mDefaultMarginSizePixels = defaultMarginPixels; + mWideMarginSizePixels = wideMarginPixels; + } + + @Override + public void onDisplayStyleChanged(@UiConfig.DisplayStyle int newDisplayStyle) { + mCurrentDisplayStyle = newDisplayStyle; + updateMargins(); + } + + /** + * Sets the lateral margins on the associated view, using the appropriate value depending on + * the current display style. + * @param defaultMarginPixels Margin size to use in {@link UiConfig#DISPLAY_STYLE_REGULAR}. + * @param wideMarginPixels Margin size to use in {@link UiConfig#DISPLAY_STYLE_WIDE}. + */ + public void setMargins(int defaultMarginPixels, int wideMarginPixels) { + mDefaultMarginSizePixels = defaultMarginPixels; + mWideMarginSizePixels = wideMarginPixels; + updateMargins(); + } + + private void updateMargins() { + MarginLayoutParams layoutParams = (MarginLayoutParams) mView.getLayoutParams(); + if (mCurrentDisplayStyle == UiConfig.DISPLAY_STYLE_WIDE) { + layoutParams.setMargins(mWideMarginSizePixels, layoutParams.topMargin, + mWideMarginSizePixels, layoutParams.bottomMargin); + } else { + layoutParams.setMargins(mDefaultMarginSizePixels, layoutParams.topMargin, + mDefaultMarginSizePixels, layoutParams.bottomMargin); + } + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/OWNERS new file mode 100644 index 0000000..78c3488 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/OWNERS
@@ -0,0 +1 @@ +file://chrome/android/java/src/org/chromium/chrome/browser/ntp/OWNERS
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/UiConfig.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/UiConfig.java similarity index 89% rename from chrome/android/java/src/org/chromium/chrome/browser/ntp/UiConfig.java rename to chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/UiConfig.java index 3dd87ef..8b36c81 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/UiConfig.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/displaystyle/UiConfig.java
@@ -2,7 +2,7 @@ // 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.ntp; +package org.chromium.chrome.browser.widget.displaystyle; import android.annotation.SuppressLint; import android.content.Context; @@ -19,7 +19,7 @@ import java.util.List; /** - * Exposes general configuration info about the NTP UI. + * Exposes general configuration info about the display style for a given reference View. */ public class UiConfig { /** The different supported UI setups. Observers can register to be notified of changes.*/ @@ -32,10 +32,10 @@ public static final int DISPLAY_STYLE_REGULAR = 1; public static final int DISPLAY_STYLE_WIDE = 2; - private static final int REGULAR_CARD_MIN_WIDTH_DP = 360; - private static final int WIDE_CARD_MIN_WIDTH_DP = 600; + public static final int REGULAR_DISPLAY_STYLE_MIN_WIDTH_DP = 360; + public static final int WIDE_DISPLAY_STYLE_MIN_WIDTH_DP = 600; - private static final String TAG = "Ntp"; + private static final String TAG = "DisplayStyle"; private static final boolean DEBUG = false; @DisplayStyle @@ -87,8 +87,6 @@ } private void updateDisplayStyle(@DisplayStyle int displayStyle) { - if (displayStyle == mCurrentDisplayStyle) return; - mCurrentDisplayStyle = displayStyle; for (DisplayStyleObserver observer : mObservers) { observer.onDisplayStyleChanged(displayStyle); @@ -113,10 +111,10 @@ @DisplayStyle int newDisplayStyle; - if (widthDp < REGULAR_CARD_MIN_WIDTH_DP) { + if (widthDp < REGULAR_DISPLAY_STYLE_MIN_WIDTH_DP) { newDisplayStyle = DISPLAY_STYLE_NARROW; if (DEBUG) debugString = String.format("DISPLAY_STYLE_NARROW (w=%ddp)", widthDp); - } else if (widthDp >= WIDE_CARD_MIN_WIDTH_DP) { + } else if (widthDp >= WIDE_DISPLAY_STYLE_MIN_WIDTH_DP) { newDisplayStyle = DISPLAY_STYLE_WIDE; if (DEBUG) debugString = String.format("DISPLAY_STYLE_WIDE (w=%ddp)", widthDp); } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java index 39e657a41b..5b19a225 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
@@ -5,6 +5,8 @@ package org.chromium.chrome.browser.widget.selection; import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.support.v4.widget.DrawerLayout; import android.support.v7.widget.LinearLayoutManager; @@ -24,20 +26,24 @@ import org.chromium.chrome.browser.widget.FadingShadow; import org.chromium.chrome.browser.widget.FadingShadowView; import org.chromium.chrome.browser.widget.LoadingView; +import org.chromium.chrome.browser.widget.displaystyle.DisplayStyleObserver; +import org.chromium.chrome.browser.widget.displaystyle.UiConfig; import org.chromium.ui.base.DeviceFormFactor; import javax.annotation.Nullable; /** * Contains UI elements common to selectable list views: a loading view, empty view, selection - * toolbar, shadow, and recycler view. + * toolbar, shadow, and RecyclerView. * * After the SelectableListLayout is inflated, it should be initialized through calls to * #initializeRecyclerView(), #initializeToolbar(), and #initializeEmptyView(). * * @param <E> The type of the selectable items this layout holds. */ -public class SelectableListLayout<E> extends RelativeLayout { +public class SelectableListLayout<E> extends RelativeLayout implements DisplayStyleObserver { + private static final int WIDE_DISPLAY_MIN_PADDING_DP = 16; + private Adapter<RecyclerView.ViewHolder> mAdapter; private ViewStub mToolbarStub; private TextView mEmptyView; @@ -45,6 +51,8 @@ private RecyclerView mRecyclerView; SelectableListToolbar<E> mToolbar; + private UiConfig mUiConfig; + private final AdapterDataObserver mAdapterObserver = new AdapterDataObserver() { @Override public void onChanged() { @@ -83,6 +91,13 @@ setFocusableInTouchMode(true); } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + if (mUiConfig != null) mUiConfig.updateDisplayStyle(); + } + /** * Initializes the RecyclerView. * @@ -170,4 +185,55 @@ public void onDestroyed() { mAdapter.unregisterAdapterDataObserver(mAdapterObserver); } + + /** + * When this layout has a wide display style, it will be width constrained to + * {@link UiConfig#WIDE_DISPLAY_STYLE_MIN_WIDTH_DP}. If the current screen width is greater than + * UiConfig#WIDE_DISPLAY_STYLE_MIN_WIDTH_DP, the SelectableListLayout will be visually centered + * by adding padding to both sides. + * + * This method should be called after the toolbar and RecyclerView are initialized. + * + * @param wideDisplayToolbarLateralOffsetPx The offset to use for the toolbar's lateral padding + * when in {@link UiConfig#DISPLAY_STYLE_WIDE}. + */ + public void setHasWideDisplayStyle(int wideDisplayToolbarLateralOffsetPx) { + mUiConfig = new UiConfig(this); + mToolbar.setHasWideDisplayStyle(wideDisplayToolbarLateralOffsetPx, mUiConfig); + mUiConfig.addObserver(this); + } + + /** + * @return The {@link UiConfig} associated with this View if one has been created, or null. + */ + @Nullable + public UiConfig getUiConfig() { + return mUiConfig; + } + + @Override + public void onDisplayStyleChanged(int newDisplayStyle) { + int padding = getPaddingForDisplayStyle(newDisplayStyle, getResources()); + + ApiCompatibilityUtils.setPaddingRelative(mRecyclerView, + padding, mRecyclerView.getPaddingTop(), + padding, mRecyclerView.getPaddingBottom()); + } + + /** + * @param displayStyle See UiConfig.DisplayStyle. + * @param resources The {@link Resources} used to retrieve configuration and display metrics. + * @return The lateral padding to use for the current display style. + */ + public static int getPaddingForDisplayStyle(int displayStyle, Resources resources) { + int padding = 0; + if (displayStyle == UiConfig.DISPLAY_STYLE_WIDE) { + int screenWidthDp = resources.getConfiguration().screenWidthDp; + float dpToPx = resources.getDisplayMetrics().density; + padding = (int) (((screenWidthDp - UiConfig.WIDE_DISPLAY_STYLE_MIN_WIDTH_DP) / 2.f) + * dpToPx); + padding = (int) Math.max(WIDE_DISPLAY_MIN_PADDING_DP * dpToPx, padding); + } + return padding; + } } \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java index dd4d18a..24aababd 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java
@@ -31,6 +31,8 @@ import org.chromium.chrome.browser.widget.NumberRollView; import org.chromium.chrome.browser.widget.TintedDrawable; import org.chromium.chrome.browser.widget.TintedImageButton; +import org.chromium.chrome.browser.widget.displaystyle.DisplayStyleObserver; +import org.chromium.chrome.browser.widget.displaystyle.UiConfig; import org.chromium.chrome.browser.widget.selection.SelectionDelegate.SelectionObserver; import org.chromium.ui.UiUtils; @@ -46,7 +48,7 @@ * @param <E> The type of the selectable items this toolbar interacts with. */ public class SelectableListToolbar<E> extends Toolbar implements SelectionObserver<E>, - OnClickListener, OnEditorActionListener { + OnClickListener, OnEditorActionListener, DisplayStyleObserver { /** * A delegate that handles searching the list of selectable items associated with this toolbar. @@ -99,6 +101,16 @@ private int mSelectionBackgroundColor; private int mSearchBackgroundColor; + private UiConfig mUiConfig; + private int mDefaultTitleMarginStartPx; + private int mWideDisplayLateralOffsetPx; + private int mWideDisplayEndOffsetPx; + private int mWideDisplayNavButtonOffsetPx; + private int mOriginalContentInsetStart; + private int mOriginalContentInsetEnd; + private int mOriginalContentInsetStartWithNavigation; + private int mOriginalContentInsetEndWithActions; + /** * Constructor for inflating from XML. */ @@ -215,6 +227,11 @@ LayoutInflater.from(getContext()).inflate(R.layout.number_roll_view, this); mNumberRollView = (NumberRollView) findViewById(R.id.selection_mode_number); mNumberRollView.setContentDescriptionString(R.plurals.accessibility_selected_items); + + mOriginalContentInsetStart = getContentInsetStart(); + mOriginalContentInsetEnd = getContentInsetEnd(); + mOriginalContentInsetStartWithNavigation = getContentInsetStartWithNavigation(); + mOriginalContentInsetEndWithActions = getContentInsetEndWithActions(); } @Override @@ -376,6 +393,68 @@ } /** + * When the toolbar has a wide display style, its contents will be width constrained to + * {@link UiConfig#WIDE_DISPLAY_STYLE_MIN_WIDTH_DP}. If the current screen width is greater than + * UiConfig#WIDE_DISPLAY_STYLE_MIN_WIDTH_DP, the toolbar contents will be visually centered by + * adding padding to both sides. + * + * @param wideDisplayLateralOffsetPx The offset to use for the lateral padding when in + * {@link UiConfig#DISPLAY_STYLE_WIDE}. + */ + public void setHasWideDisplayStyle(int wideDisplayLateralOffsetPx, UiConfig uiConfig) { + mWideDisplayLateralOffsetPx = wideDisplayLateralOffsetPx; + mDefaultTitleMarginStartPx = getTitleMarginStart(); + mWideDisplayNavButtonOffsetPx = + getResources().getDimensionPixelSize(R.dimen.toolbar_wide_display_nav_icon_offset); + mWideDisplayEndOffsetPx = getResources().getDimensionPixelSize( + R.dimen.toolbar_wide_display_end_offset); + + mUiConfig = uiConfig; + mUiConfig.addObserver(this); + + } + + @Override + public void onDisplayStyleChanged(int newDisplayStyle) { + int padding = + SelectableListLayout.getPaddingForDisplayStyle(newDisplayStyle, getResources()); + int paddingStartOffset = 0; + int paddingEndOffset = 0; + int contentInsetStart = mOriginalContentInsetStart; + int contentInsetStartWithNavigation = mOriginalContentInsetStartWithNavigation; + int contentInsetEnd = mOriginalContentInsetEnd; + int contentInsetEndWithActions = mOriginalContentInsetEndWithActions; + + if (newDisplayStyle == UiConfig.DISPLAY_STYLE_WIDE) { + paddingStartOffset = mWideDisplayLateralOffsetPx; + + // The title and nav buttons are inset in the normal display style. In the wide display + // style they should be aligned with the starting edge of the list elements. + if (mIsSearching || mIsSelectionEnabled) { + paddingStartOffset += mWideDisplayNavButtonOffsetPx; + } else { + paddingStartOffset -= mDefaultTitleMarginStartPx; + } + + // The end button is also inset in the normal display. In the wide display it should be + // aligned with the ending edge of the list elements. + paddingEndOffset = mWideDisplayLateralOffsetPx + mWideDisplayEndOffsetPx; + + contentInsetStart = 0; + contentInsetStartWithNavigation = 0; + contentInsetEnd = 0; + contentInsetEndWithActions = 0; + } + + ApiCompatibilityUtils.setPaddingRelative(this, + padding + paddingStartOffset, this.getPaddingTop(), + padding + paddingEndOffset, this.getPaddingBottom()); + setContentInsetsRelative(contentInsetStart, contentInsetEnd); + setContentInsetStartWithNavigation(contentInsetStartWithNavigation); + setContentInsetEndWithActions(contentInsetEndWithActions); + } + + /** * Set up ActionBarDrawerToggle, a.k.a. hamburger button. */ private void initActionBarDrawerToggle() { @@ -401,6 +480,8 @@ mNumberRollView.setVisibility(View.GONE); mNumberRollView.setNumber(0, false); + + updateDisplayStyleIfNecessary(); } private void showSelectionView(List<E> selectedItems, boolean wasSelectionEnabled) { @@ -418,6 +499,8 @@ mNumberRollView.setNumber(selectedItems.size(), true); if (mIsSearching) UiUtils.hideKeyboard(mSearchEditText); + + updateDisplayStyleIfNecessary(); } private void showSearchViewInternal() { @@ -427,6 +510,12 @@ setNavigationButton(NAVIGATION_BUTTON_BACK); setBackgroundColor(mSearchBackgroundColor); + + updateDisplayStyleIfNecessary(); + } + + private void updateDisplayStyleIfNecessary() { + if (mUiConfig != null) onDisplayStyleChanged(mUiConfig.getCurrentDisplayStyle()); } @VisibleForTesting
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni index f0601ff..97ac3fdff 100644 --- a/chrome/android/java_sources.gni +++ b/chrome/android/java_sources.gni
@@ -553,7 +553,6 @@ "java/src/org/chromium/chrome/browser/notifications/WebApkNotificationClient.java", "java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java", "java/src/org/chromium/chrome/browser/ntp/ContextMenuManager.java", - "java/src/org/chromium/chrome/browser/ntp/DisplayStyleObserver.java", "java/src/org/chromium/chrome/browser/ntp/FakeRecentlyClosedTabManager.java", "java/src/org/chromium/chrome/browser/ntp/ForeignSessionHelper.java", "java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPage.java", @@ -582,20 +581,17 @@ "java/src/org/chromium/chrome/browser/ntp/RecentlyClosedTab.java", "java/src/org/chromium/chrome/browser/ntp/RecentlyClosedTabManager.java", "java/src/org/chromium/chrome/browser/ntp/TitleUtil.java", - "java/src/org/chromium/chrome/browser/ntp/UiConfig.java", "java/src/org/chromium/chrome/browser/ntp/cards/AboveTheFoldItem.java", "java/src/org/chromium/chrome/browser/ntp/cards/ActionItem.java", "java/src/org/chromium/chrome/browser/ntp/cards/AllDismissedItem.java", "java/src/org/chromium/chrome/browser/ntp/cards/CardViewHolder.java", "java/src/org/chromium/chrome/browser/ntp/cards/CardsVariationParameters.java", "java/src/org/chromium/chrome/browser/ntp/cards/ChildNode.java", - "java/src/org/chromium/chrome/browser/ntp/cards/DisplayStyleObserverAdapter.java", "java/src/org/chromium/chrome/browser/ntp/cards/InnerNode.java", "java/src/org/chromium/chrome/browser/ntp/cards/ImpressionTracker.java", "java/src/org/chromium/chrome/browser/ntp/cards/ItemViewType.java", "java/src/org/chromium/chrome/browser/ntp/cards/Leaf.java", "java/src/org/chromium/chrome/browser/ntp/cards/Footer.java", - "java/src/org/chromium/chrome/browser/ntp/cards/MarginResizer.java", "java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java", "java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java", "java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageViewHolder.java", @@ -712,6 +708,7 @@ "java/src/org/chromium/chrome/browser/payments/ui/BillingAddressAdapter.java", "java/src/org/chromium/chrome/browser/payments/ui/Completable.java", "java/src/org/chromium/chrome/browser/payments/ui/ContactDetailsSection.java", + "java/src/org/chromium/chrome/browser/payments/ui/DropdownFieldAdapter.java", "java/src/org/chromium/chrome/browser/payments/ui/EditorDialogToolbar.java", "java/src/org/chromium/chrome/browser/payments/ui/EditorDropdownField.java", "java/src/org/chromium/chrome/browser/payments/ui/EditorFieldModel.java", @@ -1134,6 +1131,10 @@ "java/src/org/chromium/chrome/browser/widget/animation/AnimatorProperties.java", "java/src/org/chromium/chrome/browser/widget/animation/CancelAwareAnimatorListener.java", "java/src/org/chromium/chrome/browser/widget/animation/FocusAnimator.java", + "java/src/org/chromium/chrome/browser/widget/displaystyle/MarginResizer.java", + "java/src/org/chromium/chrome/browser/widget/displaystyle/DisplayStyleObserver.java", + "java/src/org/chromium/chrome/browser/widget/displaystyle/DisplayStyleObserverAdapter.java", + "java/src/org/chromium/chrome/browser/widget/displaystyle/UiConfig.java", "java/src/org/chromium/chrome/browser/widget/emptybackground/EmptyBackgroundViewTablet.java", "java/src/org/chromium/chrome/browser/widget/emptybackground/EmptyBackgroundViewWrapper.java", "java/src/org/chromium/chrome/browser/widget/findinpage/FindResultBar.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistenceIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistenceIntegrationTest.java index 2b9f016..47c34992 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistenceIntegrationTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistenceIntegrationTest.java
@@ -24,7 +24,7 @@ public void startMainActivity() throws InterruptedException { super.startMainActivity(); startCustomTabActivityWithIntent(CustomTabsTestUtils.createMinimalCustomTabIntent( - getInstrumentation().getTargetContext(), UrlConstants.ABOUT_BLANK)); + getInstrumentation().getTargetContext(), UrlConstants.ABOUT_BLANK_DISPLAY_URL)); } @MediumTest
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java index f50d093..9cf9d7d 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java
@@ -99,7 +99,7 @@ * Leaves and destroys the {@link RecentTabsPage} by navigating the tab to {@code about:blank}. */ private void leaveRecentTabsPage() throws InterruptedException { - loadUrl(UrlConstants.ABOUT_BLANK); + loadUrl(UrlConstants.ABOUT_BLANK_DISPLAY_URL); CriteriaHelper.pollUiThread(new Criteria("RecentTabsPage is still there") { @Override public boolean isSatisfied() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java index 77f946e..035184630c 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
@@ -21,7 +21,6 @@ import org.chromium.chrome.browser.favicon.FaviconHelper.IconAvailabilityCallback; import org.chromium.chrome.browser.favicon.LargeIconBridge.LargeIconCallback; import org.chromium.chrome.browser.ntp.NewTabPage.DestructionObserver; -import org.chromium.chrome.browser.ntp.UiConfig; import org.chromium.chrome.browser.ntp.cards.ActionItem; import org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter; import org.chromium.chrome.browser.ntp.cards.NewTabPageRecyclerView; @@ -32,6 +31,7 @@ import org.chromium.chrome.browser.suggestions.SuggestionsNavigationDelegate; import org.chromium.chrome.browser.suggestions.SuggestionsRanker; import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate; +import org.chromium.chrome.browser.widget.displaystyle.UiConfig; import org.chromium.chrome.test.ChromeActivityTestCaseBase; import org.chromium.chrome.test.util.RenderUtils.ViewRenderer;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java index 273dd94..198e424e 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
@@ -4,11 +4,14 @@ package org.chromium.chrome.browser.ntp.cards; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.collection.IsIterableContainingInOrder.contains; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -721,6 +724,36 @@ @Test @Feature({"Ntp"}) + @EnableFeatures(ChromeFeatureList.NTP_SUGGESTIONS_SECTION_DISMISSAL) + public void testGetItemDismissalGroupWithSuggestions() { + List<SnippetArticle> suggestions = createDummySuggestions(5, TEST_CATEGORY_ID); + SuggestionsSection section = createSectionWithReloadAction(false); + section.setSuggestions(suggestions, CategoryStatus.AVAILABLE, /* replaceExisting = */ true); + + assertThat(section.getItemDismissalGroup(1).size(), is(1)); + assertThat(section.getItemDismissalGroup(1), contains(1)); + } + + @Test + @Feature({"Ntp"}) + @EnableFeatures(ChromeFeatureList.NTP_SUGGESTIONS_SECTION_DISMISSAL) + public void testGetItemDismissalGroupWithActionItem() { + SuggestionsSection section = createSectionWithReloadAction(true); + assertThat(section.getItemDismissalGroup(1).size(), is(2)); + assertThat(section.getItemDismissalGroup(1), contains(1, 2)); + } + + @Test + @Feature({"Ntp"}) + @EnableFeatures(ChromeFeatureList.NTP_SUGGESTIONS_SECTION_DISMISSAL) + public void testGetItemDismissalGroupWithoutActionItem() { + SuggestionsSection section = createSectionWithReloadAction(false); + assertThat(section.getItemDismissalGroup(1).size(), is(1)); + assertThat(section.getItemDismissalGroup(1), contains(1)); + } + + @Test + @Feature({"Ntp"}) public void testCardIsNotifiedWhenNotTheLastAnymore() { List<SnippetArticle> suggestions = createDummySuggestions(5, /* categoryId = */ 42); SuggestionsSection section = createSectionWithReloadAction(false);
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h index 8603eea2..0bc227c8 100644 --- a/chrome/app/chrome_command_ids.h +++ b/chrome/app/chrome_command_ids.h
@@ -335,6 +335,7 @@ #define IDC_MEDIA_ROUTER_ALWAYS_SHOW_TOOLBAR_ACTION 51204 #define IDC_MEDIA_ROUTER_CLOUD_SERVICES_TOGGLE 51205 #define IDC_MEDIA_ROUTER_MANAGE_DEVICES 51206 +#define IDC_MEDIA_ROUTER_SHOWN_BY_POLICY 51207 // Context menu items for media stream status tray #define IDC_MEDIA_STREAM_DEVICE_STATUS_TRAY 51300
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index d2403d2..8fb5016 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp
@@ -88,6 +88,12 @@ <message name="IDS_FLAGS_NEW_ZIP_UNPACKER_DESCRIPTION" desc="Description of about::flags option for the new ZIP unpacker based on the File System Provider API."> New ZIP unpacker flow, based on the File System Provider API. </message> + <message name="IDS_FLAGS_SHOW_ARC_FILES_APP_NAME" desc="Name of about::flags option for showing Android Files app in launcher."> + Show Android Files app + </message> + <message name="IDS_FLAGS_SHOW_ARC_FILES_APP_DESCRIPTION" desc="Description of the about::flag option for showing ARC Files app in launcher."> + Show Android Files app in Chrome OS launcher. This is only effective on a device with access to Play Store. + </message> <message name="IDS_FILE_SYSTEM_PROVIDER_UNRESPONSIVE_WARNING" desc="A warning shown in a notification that an operation is taking longer than expected."> An operation is taking longer than expected. Do you want to abort it? </message>
diff --git a/chrome/app/media_router_strings.grdp b/chrome/app/media_router_strings.grdp index 80c62614..36a6941 100644 --- a/chrome/app/media_router_strings.grdp +++ b/chrome/app/media_router_strings.grdp
@@ -73,6 +73,9 @@ <message name="IDS_MEDIA_ROUTER_MANAGE_DEVICES" desc="Title of a menu item which, on click, opens a page that allows the user to manage Cast devices: view, reboot, reset, and manage settings of available Cast devices, or set up new Cast devices."> Manage Cast devices </message> + <message name="IDS_MEDIA_ROUTER_SHOWN_BY_POLICY" desc="Title of a menu item, which indicates that the Media Router action icon was added to the toolbar by the policy enabled by the user's administrator."> + Added by your administrator + </message> <!-- First Run Flow --> <message name="IDS_MEDIA_ROUTER_FIRST_RUN_FLOW_CLOUD_PREF_TEXT" desc="Text of the cloud services preferences text in the first run flow, which is shown to the user if they are currently signed in and have enabled sync.">
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp index 7abce3d..d4adc756 100644 --- a/chrome/app/settings_strings.grdp +++ b/chrome/app/settings_strings.grdp
@@ -517,6 +517,12 @@ <message name="IDS_SETTINGS_BLUETOOTH_CONFIRM_PASSKEY" desc="Bluetooth pairing message typically shown when pairing with a device that has a display."> Please confirm this passkey is shown on "<ph name="DEVICE_NAME">$1<ex>Nexus S</ex></ph>": </message> + <message name="IDS_SETTINGS_BLUETOOTH_OFF" desc="In Bluetooth page, the label when Bluetooth is off (disabled)."> + Off + </message> + <message name="IDS_SETTINGS_BLUETOOTH_ON" desc="In Bluetooth page, the label when Bluetooth is on (enabled)."> + On + </message> <message name="IDS_SETTINGS_BLUETOOTH_CONNECTED" desc="In Bluetooth device list, this label is shown below a device which is already connected."> Connected </message>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 9a6f7b0b..38d3592 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -2222,6 +2222,13 @@ IDS_FLAGS_GDI_TEXT_PRINTING_DESCRIPTION, kOsWin, FEATURE_VALUE_TYPE(features::kGdiTextPrinting)} #endif + +#if defined(OS_CHROMEOS) + {"show-arc-files-app", IDS_FLAGS_SHOW_ARC_FILES_APP_NAME, + IDS_FLAGS_SHOW_ARC_FILES_APP_DESCRIPTION, kOsCrOS, + FEATURE_VALUE_TYPE(arc::kShowArcFilesAppFeature)}, +#endif // defined(OS_CHROMEOS) + // NOTE: Adding new command-line switches requires adding corresponding // entries to enum "LoginCustomFlags" in histograms.xml. See note in // histograms.xml and don't forget to run AboutFlagsHistogramTest unit test.
diff --git a/chrome/browser/android/chrome_entry_point.cc b/chrome/browser/android/chrome_entry_point.cc index 5bc9215..4a540518 100644 --- a/chrome/browser/android/chrome_entry_point.cc +++ b/chrome/browser/android/chrome_entry_point.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/android/jni_android.h" +#include "base/android/jni_utils.h" #include "base/android/library_loader/library_loader_hooks.h" #include "base/bind.h" #include "chrome/app/android/chrome_jni_onload.h" @@ -25,6 +26,15 @@ // This is called by the VM when the shared library is first loaded. JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { + // By default, all JNI methods are registered. However, since render processes + // don't need very much Java code, we enable selective JNI registration on the + // Java side and only register a subset of JNI methods. + base::android::InitVM(vm); + JNIEnv* env = base::android::AttachCurrentThread(); + if (base::android::isSelectiveJniRegistrationEnabled(env)) { + base::android::SetJniRegistrationType( + base::android::SELECTIVE_JNI_REGISTRATION); + } if (!android::OnJNIOnLoadRegisterJNI(vm, base::Bind(&RegisterJNI))) { return -1; }
diff --git a/chrome/browser/android/monochrome_entry_point.cc b/chrome/browser/android/monochrome_entry_point.cc index a3c7f304..7a04309c 100644 --- a/chrome/browser/android/monochrome_entry_point.cc +++ b/chrome/browser/android/monochrome_entry_point.cc
@@ -36,8 +36,8 @@ // This is called by the VM when the shared library is first loaded. JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { - base::android::DisableManualJniRegistration(); base::android::InitVM(vm); + base::android::SetJniRegistrationType(base::android::NO_JNI_REGISTRATION); base::android::SetNativeInitializationHook(NativeInit); return JNI_VERSION_1_4; }
diff --git a/chrome/browser/android/webapk/webapk_installer.cc b/chrome/browser/android/webapk/webapk_installer.cc index cf8c3e5..161a388 100644 --- a/chrome/browser/android/webapk/webapk_installer.cc +++ b/chrome/browser/android/webapk/webapk_installer.cc
@@ -28,6 +28,7 @@ #include "content/public/browser/browser_thread.h" #include "content/public/common/manifest_util.h" #include "jni/WebApkInstaller_jni.h" +#include "net/base/load_flags.h" #include "net/http/http_status_code.h" #include "net/url_request/url_fetcher.h" #include "ui/gfx/codec/png_codec.h" @@ -507,6 +508,10 @@ std::string serialized_request; request_proto->SerializeToString(&serialized_request); url_fetcher_->SetUploadData(kProtoMimeType, serialized_request); + url_fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE | + net::LOAD_DO_NOT_SEND_COOKIES | + net::LOAD_DO_NOT_SAVE_COOKIES | + net::LOAD_DO_NOT_SEND_AUTH_DATA); url_fetcher_->Start(); }
diff --git a/chrome/browser/browser_about_handler.cc b/chrome/browser/browser_about_handler.cc index 6006a1b..638c2e8 100644 --- a/chrome/browser/browser_about_handler.cc +++ b/chrome/browser/browser_about_handler.cc
@@ -118,8 +118,7 @@ // Redirect chrome://help, unless MD settings is enabled. } else if (host == chrome::kChromeUIHelpHost) { if (base::FeatureList::IsEnabled(features::kMaterialDesignSettings)) { - host = chrome::kChromeUISettingsHost; - path = chrome::kChromeUIHelpHost; + return false; // Handled in the HandleWebUI handler. } else if (::switches::AboutInSettingsEnabled()) { host = chrome::kChromeUISettingsFrameHost; if (url->path().empty() || url->path() == "/") @@ -128,12 +127,6 @@ host = chrome::kChromeUIUberHost; path = chrome::kChromeUIHelpHost + url->path(); } - // Redirect chrome://chrome to chrome://settings/help, only for MD settings. - } else if (host == chrome::kChromeUIUberHost && - base::FeatureList::IsEnabled(features::kMaterialDesignSettings) && - (url->path().empty() || url->path() == "/")) { - host = chrome::kChromeUISettingsHost; - path = chrome::kChromeUIHelpHost; } GURL::Replacements replacements;
diff --git a/chrome/browser/browser_about_handler_unittest.cc b/chrome/browser/browser_about_handler_unittest.cc index 06f5378..f9df2da 100644 --- a/chrome/browser/browser_about_handler_unittest.cc +++ b/chrome/browser/browser_about_handler_unittest.cc
@@ -120,10 +120,7 @@ chrome_prefix.append(url::kStandardSchemeSeparator); std::vector<AboutURLTestCase> test_cases( {{GURL(chrome_prefix + chrome::kChromeUISettingsHost), - GURL(chrome_prefix + chrome::kChromeUISettingsHost)}, - {GURL(chrome_prefix + chrome::kChromeUIHelpHost), - GURL(chrome_prefix + chrome::kChromeUISettingsHost + "/" + - chrome::kChromeUIHelpHost)}}); + GURL(chrome_prefix + chrome::kChromeUISettingsHost)}}); TestWillHandleBrowserAboutURL(test_cases); }
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index b8ee4de6..5660e22 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -532,6 +532,17 @@ // Handles rewriting Web UI URLs. bool HandleWebUI(GURL* url, content::BrowserContext* browser_context) { + if (base::FeatureList::IsEnabled(features::kMaterialDesignSettings)) { + // Rewrite chrome://help and chrome://chrome to chrome://settings/help. + if (url->host() == chrome::kChromeUIHelpHost || + (url->host() == chrome::kChromeUIUberHost && + (url->path().empty() || url->path() == "/"))) { + *url = ReplaceURLHostAndPath(*url, chrome::kChromeUISettingsHost, + chrome::kChromeUIHelpHost); + return true; // Return true to update the displayed URL. + } + } + // Do not handle special URLs such as "about:foo" if (!url->host().empty()) { const GURL chrome_url = AddUberHost(*url); @@ -567,6 +578,13 @@ // Reverse URL handler for Web UI. Maps "chrome://chrome/foo/" to // "chrome://foo/". bool HandleWebUIReverse(GURL* url, content::BrowserContext* browser_context) { + // No need to actually reverse-rewrite the URL, but return true to update the + // displayed URL when rewriting chrome://help to chrome://settings/help. + if (base::FeatureList::IsEnabled(features::kMaterialDesignSettings) && + url->host() == chrome::kChromeUISettingsHost) { + return true; + } + if (!url->is_valid() || !url->SchemeIs(content::kChromeUIScheme)) return false;
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index a181786..f61d1bb 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -283,8 +283,12 @@ "arc/optin/arc_optin_preference_handler.cc", "arc/optin/arc_optin_preference_handler.h", "arc/optin/arc_optin_preference_handler_delegate.h", + "arc/optin/arc_terms_of_service_default_negotiator.cc", + "arc/optin/arc_terms_of_service_default_negotiator.h", "arc/optin/arc_terms_of_service_negotiator.cc", "arc/optin/arc_terms_of_service_negotiator.h", + "arc/optin/arc_terms_of_service_oobe_negotiator.cc", + "arc/optin/arc_terms_of_service_oobe_negotiator.h", "arc/policy/arc_android_management_checker.cc", "arc/policy/arc_android_management_checker.h", "arc/policy/arc_android_management_checker_delegate.h", @@ -762,6 +766,7 @@ "login/screens/arc_terms_of_service_screen.cc", "login/screens/arc_terms_of_service_screen.h", "login/screens/arc_terms_of_service_screen_actor.h", + "login/screens/arc_terms_of_service_screen_actor_observer.h", "login/screens/base_screen.cc", "login/screens/base_screen.h", "login/screens/base_screen_delegate.h", @@ -1436,6 +1441,15 @@ } } +static_library("arc_test_support") { + testonly = true + + sources = [ + "arc/test/arc_data_removed_waiter.cc", + "arc/test/arc_data_removed_waiter.h", + ] +} + source_set("unit_tests") { testonly = true @@ -1469,7 +1483,7 @@ "arc/fileapi/arc_documents_provider_util_unittest.cc", "arc/intent_helper/arc_external_protocol_dialog_unittest.cc", "arc/intent_helper/arc_navigation_throttle_unittest.cc", - "arc/optin/arc_terms_of_service_negotiator_unittest.cc", + "arc/optin/arc_terms_of_service_default_negotiator_unittest.cc", "arc/policy/arc_policy_bridge_unittest.cc", "attestation/attestation_ca_client_unittest.cc", "attestation/attestation_policy_observer_unittest.cc", @@ -1702,6 +1716,7 @@ ":chromeos", ] deps = [ + ":arc_test_support", ":attestation_proto", ":device_policy_proto", "//ash/resources",
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.cc b/chrome/browser/chromeos/arc/arc_session_manager.cc index dfacaad..f31f3593 100644 --- a/chrome/browser/chromeos/arc/arc_session_manager.cc +++ b/chrome/browser/chromeos/arc/arc_session_manager.cc
@@ -21,9 +21,11 @@ #include "chrome/browser/chromeos/arc/arc_optin_uma.h" #include "chrome/browser/chromeos/arc/arc_support_host.h" #include "chrome/browser/chromeos/arc/arc_util.h" -#include "chrome/browser/chromeos/arc/optin/arc_terms_of_service_negotiator.h" +#include "chrome/browser/chromeos/arc/optin/arc_terms_of_service_default_negotiator.h" +#include "chrome/browser/chromeos/arc/optin/arc_terms_of_service_oobe_negotiator.h" #include "chrome/browser/chromeos/arc/policy/arc_android_management_checker.h" #include "chrome/browser/chromeos/arc/policy/arc_policy_util.h" +#include "chrome/browser/chromeos/login/ui/login_display_host.h" #include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/browser/policy/profile_policy_connector_factory.h" @@ -123,6 +125,20 @@ } // static +bool ArcSessionManager::IsOobeOptInActive() { + // ARC OOBE OptIn is optional for now. Test if it exists and login host is + // active. + if (!user_manager::UserManager::Get()->IsCurrentUserNew()) + return false; + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( + chromeos::switches::kEnableArcOOBEOptIn)) + return false; + if (!chromeos::LoginDisplayHost::default_host()) + return false; + return true; +} + +// static void ArcSessionManager::DisableUIForTesting() { g_disable_ui_for_testing = true; } @@ -587,13 +603,19 @@ // 2) User accepted the Terms of service on Opt-in flow, but logged out // before ARC sign in procedure was done. Then, logs in again. if (profile_->GetPrefs()->GetBoolean(prefs::kArcTermsAccepted)) { - support_host_->ShowArcLoading(); + // Don't show UI for this progress if it was not shown. + if (support_host_->ui_page() != ArcSupportHost::UIPage::NO_PAGE) + support_host_->ShowArcLoading(); StartArcAndroidManagementCheck(); return; } - // Need user's explicit Terms Of Service agreement. - StartTermsOfServiceNegotiation(); + // Need user's explicit Terms Of Service agreement. Prevent race condition + // when ARC can be enabled before profile is synced. In last case + // OnOptInPreferenceChanged is called twice. + // TODO (crbug.com/687185) + if (state_ != State::SHOWING_TERMS_OF_SERVICE) + StartTermsOfServiceNegotiation(); } void ArcSessionManager::ShutdownSession() { @@ -759,10 +781,18 @@ } SetState(State::SHOWING_TERMS_OF_SERVICE); - if (support_host_) { + if (IsOobeOptInActive()) { + VLOG(1) << "Use OOBE negotiator."; terms_of_service_negotiator_ = - base::MakeUnique<ArcTermsOfServiceNegotiator>(profile_->GetPrefs(), - support_host_.get()); + base::MakeUnique<ArcTermsOfServiceOobeNegotiator>(); + } else if (support_host_) { + VLOG(1) << "Use default negotiator."; + terms_of_service_negotiator_ = + base::MakeUnique<ArcTermsOfServiceDefaultNegotiator>( + profile_->GetPrefs(), support_host_.get()); + } + + if (terms_of_service_negotiator_) { terms_of_service_negotiator_->StartNegotiation( base::Bind(&ArcSessionManager::OnTermsOfServiceNegotiated, weak_ptr_factory_.GetWeakPtr())); @@ -783,7 +813,10 @@ // Terms were accepted. profile_->GetPrefs()->SetBoolean(prefs::kArcTermsAccepted, true); - support_host_->ShowArcLoading(); + // Don't show UI for this progress if it was not shown. + if (support_host_ && + support_host_->ui_page() != ArcSupportHost::UIPage::NO_PAGE) + support_host_->ShowArcLoading(); StartArcAndroidManagementCheck(); } @@ -791,7 +824,9 @@ DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(arc_session_runner_->IsStopped()); DCHECK(state_ == State::SHOWING_TERMS_OF_SERVICE || - state_ == State::CHECKING_ANDROID_MANAGEMENT); + state_ == State::CHECKING_ANDROID_MANAGEMENT || + (state_ == State::STOPPED && + profile_->GetPrefs()->GetBoolean(prefs::kArcTermsAccepted))); SetState(State::CHECKING_ANDROID_MANAGEMENT); android_management_checker_.reset(new ArcAndroidManagementChecker(
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.h b/chrome/browser/chromeos/arc/arc_session_manager.h index a38194a5..7984d74 100644 --- a/chrome/browser/chromeos/arc/arc_session_manager.h +++ b/chrome/browser/chromeos/arc/arc_session_manager.h
@@ -121,6 +121,9 @@ static ArcSessionManager* Get(); + // Exposed here for unit_tests validation. + static bool IsOobeOptInActive(); + // It is called from chrome/browser/prefs/browser_prefs.cc. static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
diff --git a/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc b/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc index 741c93af..d5bbc5f 100644 --- a/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc +++ b/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc
@@ -15,6 +15,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/arc/arc_service_launcher.h" #include "chrome/browser/chromeos/arc/arc_session_manager.h" +#include "chrome/browser/chromeos/arc/test/arc_data_removed_waiter.h" #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h" #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h" #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" @@ -93,37 +94,6 @@ DISALLOW_COPY_AND_ASSIGN(ArcSessionManagerShutdownObserver); }; -// Observer of ARC data has been removed. -class ArcSessionManagerDataRemovedObserver - : public ArcSessionManager::Observer { - public: - ArcSessionManagerDataRemovedObserver() { - ArcSessionManager::Get()->AddObserver(this); - } - - ~ArcSessionManagerDataRemovedObserver() override { - ArcSessionManager::Get()->RemoveObserver(this); - } - - void Wait() { - run_loop_.reset(new base::RunLoop); - run_loop_->Run(); - run_loop_.reset(); - } - - // ArcSessionManager::Observer: - void OnArcDataRemoved() override { - if (!run_loop_) - return; - run_loop_->Quit(); - } - - private: - std::unique_ptr<base::RunLoop> run_loop_; - - DISALLOW_COPY_AND_ASSIGN(ArcSessionManagerDataRemovedObserver); -}; - class ArcSessionManagerTest : public InProcessBrowserTest { protected: ArcSessionManagerTest() {} @@ -266,7 +236,7 @@ ArcSessionManagerShutdownObserver().Wait(); ASSERT_EQ(ArcSessionManager::State::REMOVING_DATA_DIR, ArcSessionManager::Get()->state()); - ArcSessionManagerDataRemovedObserver().Wait(); + ArcDataRemovedWaiter().Wait(); ASSERT_EQ(ArcSessionManager::State::STOPPED, ArcSessionManager::Get()->state()); }
diff --git a/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc b/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc index 477ac3ff..491a4c19 100644 --- a/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc +++ b/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
@@ -13,13 +13,21 @@ #include "base/files/scoped_temp_dir.h" #include "base/macros.h" #include "base/memory/ptr_util.h" +#include "base/observer_list.h" #include "base/run_loop.h" #include "chrome/browser/chromeos/arc/arc_optin_uma.h" #include "chrome/browser/chromeos/arc/arc_session_manager.h" +#include "chrome/browser/chromeos/arc/optin/arc_terms_of_service_oobe_negotiator.h" +#include "chrome/browser/chromeos/arc/test/arc_data_removed_waiter.h" +#include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor.h" +#include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor_observer.h" +#include "chrome/browser/chromeos/login/ui/login_display_host.h" #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h" #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h" #include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" +#include "chrome/browser/policy/profile_policy_connector.h" +#include "chrome/browser/policy/profile_policy_connector_factory.h" #include "chrome/browser/prefs/pref_service_syncable_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" @@ -49,7 +57,64 @@ namespace arc { -class ArcSessionManagerTestBase : public testing::Test { +namespace { + +class FakeLoginDisplayHost : public chromeos::LoginDisplayHost { + public: + FakeLoginDisplayHost() { + DCHECK(!chromeos::LoginDisplayHost::default_host_); + chromeos::LoginDisplayHost::default_host_ = this; + } + + ~FakeLoginDisplayHost() override { + DCHECK_EQ(chromeos::LoginDisplayHost::default_host_, this); + chromeos::LoginDisplayHost::default_host_ = nullptr; + } + + /// chromeos::LoginDisplayHost: + chromeos::LoginDisplay* CreateLoginDisplay( + chromeos::LoginDisplay::Delegate* delegate) override { + return nullptr; + } + gfx::NativeWindow GetNativeWindow() const override { return nullptr; } + chromeos::OobeUI* GetOobeUI() const override { return nullptr; } + chromeos::WebUILoginView* GetWebUILoginView() const override { + return nullptr; + } + void BeforeSessionStart() override {} + void Finalize() override {} + void OnCompleteLogin() override {} + void OpenProxySettings() override {} + void SetStatusAreaVisible(bool visible) override {} + chromeos::AutoEnrollmentController* GetAutoEnrollmentController() override { + return nullptr; + } + void StartWizard(chromeos::OobeScreen first_screen) override {} + chromeos::WizardController* GetWizardController() override { return nullptr; } + chromeos::AppLaunchController* GetAppLaunchController() override { + return nullptr; + } + void StartUserAdding(const base::Closure& completion_callback) override {} + void CancelUserAdding() override {} + void StartSignInScreen(const chromeos::LoginScreenContext& context) override { + } + void OnPreferencesChanged() override {} + void PrewarmAuthentication() override {} + void StartAppLaunch(const std::string& app_id, + bool diagnostic_mode, + bool is_auto_launch) override {} + void StartDemoAppLaunch() override {} + + private: + DISALLOW_COPY_AND_ASSIGN(FakeLoginDisplayHost); +}; + +} // namespace + +// Bool parameter is used to implement ArcSessionOobeOptInTest tests for +// managed/unmanaged users. To prevent ambiguous testing::Test inheritance +// implement derivation here, in base class. +class ArcSessionManagerTestBase : public testing::TestWithParam<bool> { public: ArcSessionManagerTestBase() : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP), @@ -97,7 +162,7 @@ } protected: - Profile* profile() { return profile_.get(); } + TestingProfile* profile() { return profile_.get(); } ArcSessionManager* arc_session_manager() { return arc_session_manager_.get(); @@ -527,4 +592,180 @@ EXPECT_TRUE(terminated); } +class ArcSessionOobeOptInTest : public ArcSessionManagerTest { + public: + ArcSessionOobeOptInTest() = default; + + protected: + void CreateLoginDisplayHost() { + fake_login_display_host_ = base::MakeUnique<FakeLoginDisplayHost>(); + } + + void CloseLoginDisplayHost() { fake_login_display_host_.reset(); } + + void AppendEnableArcOOBEOptInSwitch() { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + chromeos::switches::kEnableArcOOBEOptIn); + } + + private: + std::unique_ptr<FakeLoginDisplayHost> fake_login_display_host_; + + DISALLOW_COPY_AND_ASSIGN(ArcSessionOobeOptInTest); +}; + +TEST_F(ArcSessionOobeOptInTest, OobeOptInActive) { + // OOBE OptIn is active in case of OOBE is started for new user and ARC OOBE + // is enabled by switch. + EXPECT_FALSE(ArcSessionManager::IsOobeOptInActive()); + GetFakeUserManager()->set_current_user_new(true); + EXPECT_FALSE(ArcSessionManager::IsOobeOptInActive()); + CreateLoginDisplayHost(); + EXPECT_FALSE(ArcSessionManager::IsOobeOptInActive()); + + AppendEnableArcOOBEOptInSwitch(); + GetFakeUserManager()->set_current_user_new(false); + CloseLoginDisplayHost(); + EXPECT_FALSE(ArcSessionManager::IsOobeOptInActive()); + GetFakeUserManager()->set_current_user_new(true); + EXPECT_FALSE(ArcSessionManager::IsOobeOptInActive()); + CreateLoginDisplayHost(); + EXPECT_TRUE(ArcSessionManager::IsOobeOptInActive()); +} + +class ArcSessionOobeOptInNegotiatorTest + : public ArcSessionOobeOptInTest, + public chromeos::ArcTermsOfServiceScreenActor { + public: + ArcSessionOobeOptInNegotiatorTest() = default; + + void SetUp() override { + ArcSessionOobeOptInTest::SetUp(); + + AppendEnableArcOOBEOptInSwitch(); + + ArcTermsOfServiceOobeNegotiator::SetArcTermsOfServiceScreenActorForTesting( + this); + + GetFakeUserManager()->set_current_user_new(true); + + CreateLoginDisplayHost(); + + if (IsManagedUser()) { + policy::ProfilePolicyConnector* const connector = + policy::ProfilePolicyConnectorFactory::GetForBrowserContext( + profile()); + connector->OverrideIsManagedForTesting(true); + + profile()->GetTestingPrefService()->SetManagedPref( + prefs::kArcEnabled, new base::FundamentalValue(true)); + } + + arc_session_manager()->OnPrimaryUserProfilePrepared(profile()); + } + + void TearDown() override { + // Correctly stop service. + arc_session_manager()->Shutdown(); + + ArcTermsOfServiceOobeNegotiator::SetArcTermsOfServiceScreenActorForTesting( + nullptr); + + ArcSessionOobeOptInTest::TearDown(); + } + + protected: + bool IsManagedUser() { return GetParam(); } + + void ReportResult(bool accepted) { + for (auto& observer : observer_list_) { + if (accepted) + observer.OnAccept(); + else + observer.OnSkip(); + } + base::RunLoop().RunUntilIdle(); + } + + void ReportActorDestroyed() { + for (auto& observer : observer_list_) + observer.OnActorDestroyed(this); + base::RunLoop().RunUntilIdle(); + } + + void MaybeWaitForDataRemoved() { + // In case of managed user we no need to wait data removal because + // ArcSessionManager is initialized with arc.enabled = true already and + // request to remove ARC data is not issued. + if (IsManagedUser()) + return; + + DCHECK_EQ(ArcSessionManager::State::REMOVING_DATA_DIR, + ArcSessionManager::Get()->state()); + ArcDataRemovedWaiter().Wait(); + } + + chromeos::ArcTermsOfServiceScreenActor* actor() { return this; } + + private: + // ArcTermsOfServiceScreenActor: + void AddObserver( + chromeos::ArcTermsOfServiceScreenActorObserver* observer) override { + observer_list_.AddObserver(observer); + } + + void RemoveObserver( + chromeos::ArcTermsOfServiceScreenActorObserver* observer) override { + observer_list_.RemoveObserver(observer); + } + + void Show() override { + // To match ArcTermsOfServiceScreenHandler logic where prefs::kArcEnabled is + // set to true on showing UI. + profile()->GetPrefs()->SetBoolean(prefs::kArcEnabled, true); + } + + void Hide() override {} + + base::ObserverList<chromeos::ArcTermsOfServiceScreenActorObserver> + observer_list_; + + DISALLOW_COPY_AND_ASSIGN(ArcSessionOobeOptInNegotiatorTest); +}; + +INSTANTIATE_TEST_CASE_P(ArcSessionOobeOptInNegotiatorTestImpl, + ArcSessionOobeOptInNegotiatorTest, + ::testing::Values(true, false)); + +TEST_P(ArcSessionOobeOptInNegotiatorTest, OobeTermsAccepted) { + actor()->Show(); + MaybeWaitForDataRemoved(); + EXPECT_EQ(ArcSessionManager::State::SHOWING_TERMS_OF_SERVICE, + arc_session_manager()->state()); + ReportResult(true); + EXPECT_EQ(ArcSessionManager::State::ACTIVE, arc_session_manager()->state()); + EXPECT_TRUE(arc_session_manager()->IsArcEnabled()); +} + +TEST_P(ArcSessionOobeOptInNegotiatorTest, OobeTermsRejected) { + actor()->Show(); + MaybeWaitForDataRemoved(); + EXPECT_EQ(ArcSessionManager::State::SHOWING_TERMS_OF_SERVICE, + arc_session_manager()->state()); + ReportResult(false); + EXPECT_EQ(ArcSessionManager::State::STOPPED, arc_session_manager()->state()); + EXPECT_FALSE(!IsManagedUser() && arc_session_manager()->IsArcEnabled()); +} + +TEST_P(ArcSessionOobeOptInNegotiatorTest, OobeTermsActorDestroyed) { + actor()->Show(); + MaybeWaitForDataRemoved(); + EXPECT_EQ(ArcSessionManager::State::SHOWING_TERMS_OF_SERVICE, + arc_session_manager()->state()); + CloseLoginDisplayHost(); + ReportActorDestroyed(); + EXPECT_EQ(ArcSessionManager::State::STOPPED, arc_session_manager()->state()); + EXPECT_FALSE(!IsManagedUser() && arc_session_manager()->IsArcEnabled()); +} + } // namespace arc
diff --git a/chrome/browser/chromeos/arc/optin/arc_terms_of_service_default_negotiator.cc b/chrome/browser/chromeos/arc/optin/arc_terms_of_service_default_negotiator.cc new file mode 100644 index 0000000..ca0fe33 --- /dev/null +++ b/chrome/browser/chromeos/arc/optin/arc_terms_of_service_default_negotiator.cc
@@ -0,0 +1,93 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/arc/optin/arc_terms_of_service_default_negotiator.h" + +#include <string> + +#include "base/memory/ptr_util.h" +#include "chrome/browser/chromeos/arc/optin/arc_optin_preference_handler.h" + +namespace arc { + +ArcTermsOfServiceDefaultNegotiator::ArcTermsOfServiceDefaultNegotiator( + PrefService* pref_service, + ArcSupportHost* support_host) + : pref_service_(pref_service), support_host_(support_host) { + DCHECK(pref_service_); + DCHECK(support_host_); +} + +ArcTermsOfServiceDefaultNegotiator::~ArcTermsOfServiceDefaultNegotiator() { + support_host_->RemoveObserver(this); +} + +void ArcTermsOfServiceDefaultNegotiator::StartNegotiationImpl() { + DCHECK(!preference_handler_); + preference_handler_ = + base::MakeUnique<ArcOptInPreferenceHandler>(this, pref_service_); + // This automatically updates all preferences. + preference_handler_->Start(); + + support_host_->AddObserver(this); + support_host_->ShowTermsOfService(); +} + +void ArcTermsOfServiceDefaultNegotiator::OnWindowClosed() { + // User cancels terms-of-service agreement UI by clicking "Cancel" button + // or closing the window directly. + DCHECK(preference_handler_); + support_host_->RemoveObserver(this); + preference_handler_.reset(); + + ReportResult(false); +} + +void ArcTermsOfServiceDefaultNegotiator::OnTermsAgreed( + bool is_metrics_enabled, + bool is_backup_and_restore_enabled, + bool is_location_service_enabled) { + DCHECK(preference_handler_); + support_host_->RemoveObserver(this); + + // Update the preferences with the value passed from UI. + preference_handler_->EnableMetrics(is_metrics_enabled); + preference_handler_->EnableBackupRestore(is_backup_and_restore_enabled); + preference_handler_->EnableLocationService(is_location_service_enabled); + preference_handler_.reset(); + + ReportResult(true); +} + +void ArcTermsOfServiceDefaultNegotiator::OnAuthSucceeded( + const std::string& auth_code) { + NOTREACHED(); +} + +void ArcTermsOfServiceDefaultNegotiator::OnRetryClicked() { + support_host_->ShowTermsOfService(); +} + +void ArcTermsOfServiceDefaultNegotiator::OnSendFeedbackClicked() { + NOTREACHED(); +} + +void ArcTermsOfServiceDefaultNegotiator::OnMetricsModeChanged(bool enabled, + bool managed) { + support_host_->SetMetricsPreferenceCheckbox(enabled, managed); +} + +void ArcTermsOfServiceDefaultNegotiator::OnBackupAndRestoreModeChanged( + bool enabled, + bool managed) { + support_host_->SetBackupAndRestorePreferenceCheckbox(enabled, managed); +} + +void ArcTermsOfServiceDefaultNegotiator::OnLocationServicesModeChanged( + bool enabled, + bool managed) { + support_host_->SetLocationServicesPreferenceCheckbox(enabled, managed); +} + +} // namespace arc
diff --git a/chrome/browser/chromeos/arc/optin/arc_terms_of_service_default_negotiator.h b/chrome/browser/chromeos/arc/optin/arc_terms_of_service_default_negotiator.h new file mode 100644 index 0000000..10d8a0d --- /dev/null +++ b/chrome/browser/chromeos/arc/optin/arc_terms_of_service_default_negotiator.h
@@ -0,0 +1,63 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_ARC_OPTIN_ARC_TERMS_OF_SERVICE_DEFAULT_NEGOTIATOR_H_ +#define CHROME_BROWSER_CHROMEOS_ARC_OPTIN_ARC_TERMS_OF_SERVICE_DEFAULT_NEGOTIATOR_H_ + +#include <memory> +#include <string> + +#include "base/callback.h" +#include "base/macros.h" +#include "chrome/browser/chromeos/arc/arc_support_host.h" +#include "chrome/browser/chromeos/arc/optin/arc_optin_preference_handler_observer.h" +#include "chrome/browser/chromeos/arc/optin/arc_terms_of_service_negotiator.h" + +class PrefService; + +namespace arc { + +class ArcOptInPreferenceHandler; + +// Handles the Terms-of-service agreement user action via default OptIn UI. +class ArcTermsOfServiceDefaultNegotiator + : public ArcTermsOfServiceNegotiator, + public ArcSupportHost::Observer, + public ArcOptInPreferenceHandlerObserver { + public: + ArcTermsOfServiceDefaultNegotiator(PrefService* pref_service, + ArcSupportHost* support_host); + ~ArcTermsOfServiceDefaultNegotiator() override; + + private: + // ArcSupportHost::Observer: + void OnWindowClosed() override; + void OnTermsAgreed(bool is_metrics_enabled, + bool is_backup_and_restore_enabled, + bool is_location_service_enabled) override; + void OnAuthSucceeded(const std::string& auth_code) override; + void OnRetryClicked() override; + void OnSendFeedbackClicked() override; + + // ArcOptInPreferenceHandlerObserver: + void OnMetricsModeChanged(bool enabled, bool managed) override; + void OnBackupAndRestoreModeChanged(bool enabled, bool managed) override; + void OnLocationServicesModeChanged(bool enabled, bool managed) override; + + // ArcTermsOfServiceNegotiator: + // Shows "Terms of service" page on ARC support Chrome App. + void StartNegotiationImpl() override; + + PrefService* const pref_service_; + // Owned by ArcSessionManager. + ArcSupportHost* const support_host_; + + std::unique_ptr<ArcOptInPreferenceHandler> preference_handler_; + + DISALLOW_COPY_AND_ASSIGN(ArcTermsOfServiceDefaultNegotiator); +}; + +} // namespace arc + +#endif // CHROME_BROWSER_CHROMEOS_ARC_OPTIN_ARC_TERMS_OF_SERVICE_DEFAULT_NEGOTIATOR_H_
diff --git a/chrome/browser/chromeos/arc/optin/arc_terms_of_service_negotiator_unittest.cc b/chrome/browser/chromeos/arc/optin/arc_terms_of_service_default_negotiator_unittest.cc similarity index 91% rename from chrome/browser/chromeos/arc/optin/arc_terms_of_service_negotiator_unittest.cc rename to chrome/browser/chromeos/arc/optin/arc_terms_of_service_default_negotiator_unittest.cc index 4feb871..ce080c8d 100644 --- a/chrome/browser/chromeos/arc/optin/arc_terms_of_service_negotiator_unittest.cc +++ b/chrome/browser/chromeos/arc/optin/arc_terms_of_service_default_negotiator_unittest.cc
@@ -11,7 +11,7 @@ #include "base/run_loop.h" #include "chrome/browser/chromeos/arc/arc_support_host.h" #include "chrome/browser/chromeos/arc/extensions/fake_arc_support.h" -#include "chrome/browser/chromeos/arc/optin/arc_terms_of_service_negotiator.h" +#include "chrome/browser/chromeos/arc/optin/arc_terms_of_service_default_negotiator.h" #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h" #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h" #include "chrome/common/pref_names.h" @@ -23,10 +23,10 @@ namespace arc { -class ArcTermsOfServiceNegotiatorTest : public testing::Test { +class ArcTermsOfServiceDefaultNegotiatorTest : public testing::Test { public: - ArcTermsOfServiceNegotiatorTest() = default; - ~ArcTermsOfServiceNegotiatorTest() override = default; + ArcTermsOfServiceDefaultNegotiatorTest() = default; + ~ArcTermsOfServiceDefaultNegotiatorTest() override = default; void SetUp() override { user_manager_enabler_ = @@ -39,7 +39,7 @@ support_host_ = base::MakeUnique<ArcSupportHost>(profile_.get()); fake_arc_support_ = base::MakeUnique<FakeArcSupport>(support_host_.get()); - negotiator_ = base::MakeUnique<ArcTermsOfServiceNegotiator>( + negotiator_ = base::MakeUnique<ArcTermsOfServiceDefaultNegotiator>( profile_->GetPrefs(), support_host()); } @@ -66,7 +66,7 @@ std::unique_ptr<FakeArcSupport> fake_arc_support_; std::unique_ptr<ArcTermsOfServiceNegotiator> negotiator_; - DISALLOW_COPY_AND_ASSIGN(ArcTermsOfServiceNegotiatorTest); + DISALLOW_COPY_AND_ASSIGN(ArcTermsOfServiceDefaultNegotiatorTest); }; namespace { @@ -103,7 +103,7 @@ } // namespace -TEST_F(ArcTermsOfServiceNegotiatorTest, Accept) { +TEST_F(ArcTermsOfServiceDefaultNegotiatorTest, Accept) { // Show Terms of service page. Status status = Status::PENDING; negotiator()->StartNegotiation(UpdateStatusCallback(&status)); @@ -135,7 +135,7 @@ profile()->GetPrefs()->GetBoolean(prefs::kArcLocationServiceEnabled)); } -TEST_F(ArcTermsOfServiceNegotiatorTest, Cancel) { +TEST_F(ArcTermsOfServiceDefaultNegotiatorTest, Cancel) { // Show Terms of service page. Status status = Status::PENDING; negotiator()->StartNegotiation(UpdateStatusCallback(&status)); @@ -166,7 +166,7 @@ profile()->GetPrefs()->GetBoolean(prefs::kArcLocationServiceEnabled)); } -TEST_F(ArcTermsOfServiceNegotiatorTest, Retry) { +TEST_F(ArcTermsOfServiceDefaultNegotiatorTest, Retry) { // Show Terms of service page. Status status = Status::PENDING; negotiator()->StartNegotiation(UpdateStatusCallback(&status));
diff --git a/chrome/browser/chromeos/arc/optin/arc_terms_of_service_negotiator.cc b/chrome/browser/chromeos/arc/optin/arc_terms_of_service_negotiator.cc index 8214aa5a..3ea27f4 100644 --- a/chrome/browser/chromeos/arc/optin/arc_terms_of_service_negotiator.cc +++ b/chrome/browser/chromeos/arc/optin/arc_terms_of_service_negotiator.cc
@@ -4,95 +4,24 @@ #include "chrome/browser/chromeos/arc/optin/arc_terms_of_service_negotiator.h" -#include <string> - #include "base/callback_helpers.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "chrome/browser/chromeos/arc/optin/arc_optin_preference_handler.h" namespace arc { -ArcTermsOfServiceNegotiator::ArcTermsOfServiceNegotiator( - PrefService* pref_service, - ArcSupportHost* support_host) - : pref_service_(pref_service), support_host_(support_host) { - DCHECK(pref_service_); - DCHECK(support_host_); -} +ArcTermsOfServiceNegotiator::ArcTermsOfServiceNegotiator() = default; -ArcTermsOfServiceNegotiator::~ArcTermsOfServiceNegotiator() { - support_host_->RemoveObserver(this); -} +ArcTermsOfServiceNegotiator::~ArcTermsOfServiceNegotiator() = default; void ArcTermsOfServiceNegotiator::StartNegotiation( const NegotiationCallback& callback) { DCHECK(pending_callback_.is_null()); - DCHECK(!preference_handler_); pending_callback_ = callback; - preference_handler_ = - base::MakeUnique<ArcOptInPreferenceHandler>(this, pref_service_); - // This automatically updates all preferences. - preference_handler_->Start(); - - support_host_->AddObserver(this); - support_host_->ShowTermsOfService(); + StartNegotiationImpl(); } -void ArcTermsOfServiceNegotiator::OnWindowClosed() { +void ArcTermsOfServiceNegotiator::ReportResult(bool accepted) { DCHECK(!pending_callback_.is_null()); - DCHECK(preference_handler_); - support_host_->RemoveObserver(this); - preference_handler_.reset(); - - // User cancels terms-of-service agreement UI by clicking "Cancel" button - // or closing the window directly. - base::ResetAndReturn(&pending_callback_).Run(false); -} - -void ArcTermsOfServiceNegotiator::OnTermsAgreed( - bool is_metrics_enabled, - bool is_backup_and_restore_enabled, - bool is_location_service_enabled) { - DCHECK(!pending_callback_.is_null()); - DCHECK(preference_handler_); - support_host_->RemoveObserver(this); - - // Update the preferences with the value passed from UI. - preference_handler_->EnableMetrics(is_metrics_enabled); - preference_handler_->EnableBackupRestore(is_backup_and_restore_enabled); - preference_handler_->EnableLocationService(is_location_service_enabled); - preference_handler_.reset(); - - base::ResetAndReturn(&pending_callback_).Run(true); -} - -void ArcTermsOfServiceNegotiator::OnAuthSucceeded( - const std::string& auth_code) { - NOTREACHED(); -} - -void ArcTermsOfServiceNegotiator::OnRetryClicked() { - support_host_->ShowTermsOfService(); -} - -void ArcTermsOfServiceNegotiator::OnSendFeedbackClicked() { - NOTREACHED(); -} - -void ArcTermsOfServiceNegotiator::OnMetricsModeChanged(bool enabled, - bool managed) { - support_host_->SetMetricsPreferenceCheckbox(enabled, managed); -} - -void ArcTermsOfServiceNegotiator::OnBackupAndRestoreModeChanged(bool enabled, - bool managed) { - support_host_->SetBackupAndRestorePreferenceCheckbox(enabled, managed); -} - -void ArcTermsOfServiceNegotiator::OnLocationServicesModeChanged(bool enabled, - bool managed) { - support_host_->SetLocationServicesPreferenceCheckbox(enabled, managed); + base::ResetAndReturn(&pending_callback_).Run(accepted); } } // namespace arc
diff --git a/chrome/browser/chromeos/arc/optin/arc_terms_of_service_negotiator.h b/chrome/browser/chromeos/arc/optin/arc_terms_of_service_negotiator.h index 41871dde..53adadbe 100644 --- a/chrome/browser/chromeos/arc/optin/arc_terms_of_service_negotiator.h +++ b/chrome/browser/chromeos/arc/optin/arc_terms_of_service_negotiator.h
@@ -5,58 +5,34 @@ #ifndef CHROME_BROWSER_CHROMEOS_ARC_OPTIN_ARC_TERMS_OF_SERVICE_NEGOTIATOR_H_ #define CHROME_BROWSER_CHROMEOS_ARC_OPTIN_ARC_TERMS_OF_SERVICE_NEGOTIATOR_H_ -#include <memory> -#include <string> - #include "base/callback.h" #include "base/macros.h" -#include "chrome/browser/chromeos/arc/arc_support_host.h" -#include "chrome/browser/chromeos/arc/optin/arc_optin_preference_handler_observer.h" - -class PrefService; namespace arc { -class ArcOptInPreferenceHandler; - -// Handles the Terms-of-service agreement user action. -class ArcTermsOfServiceNegotiator : public ArcSupportHost::Observer, - public ArcOptInPreferenceHandlerObserver { +// Interface to handle the Terms-of-service agreement user action. +class ArcTermsOfServiceNegotiator { public: - ArcTermsOfServiceNegotiator(PrefService* pref_service, - ArcSupportHost* support_host); - ~ArcTermsOfServiceNegotiator() override; + ArcTermsOfServiceNegotiator(); + virtual ~ArcTermsOfServiceNegotiator(); - // Shows "Terms of service" page on ARC support Chrome App. Invokes the - // |callback| asynchronously with "|agreed| = true" if user agrees it. - // Otherwise (e.g., user clicks "Cancel" or closes the window), invokes - // |callback| with |agreed| = false. - // Deleting this instance cancels the operation, so |callback| will never - // be invoked then. + // Invokes the|callback| asynchronously with "|accepted| = true" if user + // accepts ToS. If user explicitly rejects ToS, invokes |callback| with + // |accepted| = false. Deleting this instance cancels the operation, so + // |callback| will never be invoked then. using NegotiationCallback = base::Callback<void(bool accepted)>; void StartNegotiation(const NegotiationCallback& callback); + protected: + // Reports result of negotiation via callback and then resets it. If + // |accepted| is true then this means terms of service were accepted. + void ReportResult(bool accepted); + private: - // ArcSupportHost::Observer: - void OnWindowClosed() override; - void OnTermsAgreed(bool is_metrics_enabled, - bool is_backup_and_restore_enabled, - bool is_location_service_enabled) override; - void OnAuthSucceeded(const std::string& auth_code) override; - void OnRetryClicked() override; - void OnSendFeedbackClicked() override; - - // ArcOptInPreferenceHandlerObserver: - void OnMetricsModeChanged(bool enabled, bool managed) override; - void OnBackupAndRestoreModeChanged(bool enabled, bool managed) override; - void OnLocationServicesModeChanged(bool enabled, bool managed) override; - - PrefService* const pref_service_; - // Owned by ArcSessionManager. - ArcSupportHost* const support_host_; + // Performs implementation specific action to start negotiation. + virtual void StartNegotiationImpl() = 0; NegotiationCallback pending_callback_; - std::unique_ptr<ArcOptInPreferenceHandler> preference_handler_; DISALLOW_COPY_AND_ASSIGN(ArcTermsOfServiceNegotiator); };
diff --git a/chrome/browser/chromeos/arc/optin/arc_terms_of_service_oobe_negotiator.cc b/chrome/browser/chromeos/arc/optin/arc_terms_of_service_oobe_negotiator.cc new file mode 100644 index 0000000..2e55d95 --- /dev/null +++ b/chrome/browser/chromeos/arc/optin/arc_terms_of_service_oobe_negotiator.cc
@@ -0,0 +1,71 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/arc/optin/arc_terms_of_service_oobe_negotiator.h" + +#include "base/bind.h" +#include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor.h" +#include "chrome/browser/chromeos/login/ui/login_display_host.h" +#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" + +namespace arc { + +namespace { + +chromeos::ArcTermsOfServiceScreenActor* g_actor_for_testing = nullptr; + +chromeos::ArcTermsOfServiceScreenActor* GetScreenActor() { + // Inject testing instance. + if (g_actor_for_testing) + return g_actor_for_testing; + + chromeos::LoginDisplayHost* host = chromeos::LoginDisplayHost::default_host(); + DCHECK(host); + DCHECK(host->GetOobeUI()); + return host->GetOobeUI()->GetArcTermsOfServiceScreenActor(); +} + +} // namespace + +// static +void ArcTermsOfServiceOobeNegotiator::SetArcTermsOfServiceScreenActorForTesting( + chromeos::ArcTermsOfServiceScreenActor* actor) { + g_actor_for_testing = actor; +} + +ArcTermsOfServiceOobeNegotiator::ArcTermsOfServiceOobeNegotiator() = default; + +ArcTermsOfServiceOobeNegotiator::~ArcTermsOfServiceOobeNegotiator() { + DCHECK(!screen_actor_); +} + +void ArcTermsOfServiceOobeNegotiator::StartNegotiationImpl() { + DCHECK(!screen_actor_); + screen_actor_ = GetScreenActor(); + DCHECK(screen_actor_); + screen_actor_->AddObserver(this); +} + +void ArcTermsOfServiceOobeNegotiator::HandleTermsAccepted(bool accepted) { + DCHECK(screen_actor_); + screen_actor_->RemoveObserver(this); + screen_actor_ = nullptr; + ReportResult(accepted); +} + +void ArcTermsOfServiceOobeNegotiator::OnSkip() { + HandleTermsAccepted(false); +} + +void ArcTermsOfServiceOobeNegotiator::OnAccept() { + HandleTermsAccepted(true); +} + +void ArcTermsOfServiceOobeNegotiator::OnActorDestroyed( + chromeos::ArcTermsOfServiceScreenActor* actor) { + DCHECK_EQ(actor, screen_actor_); + HandleTermsAccepted(false); +} + +} // namespace arc
diff --git a/chrome/browser/chromeos/arc/optin/arc_terms_of_service_oobe_negotiator.h b/chrome/browser/chromeos/arc/optin/arc_terms_of_service_oobe_negotiator.h new file mode 100644 index 0000000..df6f470 --- /dev/null +++ b/chrome/browser/chromeos/arc/optin/arc_terms_of_service_oobe_negotiator.h
@@ -0,0 +1,60 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_ARC_OPTIN_ARC_TERMS_OF_SERVICE_OOBE_NEGOTIATOR_H_ +#define CHROME_BROWSER_CHROMEOS_ARC_OPTIN_ARC_TERMS_OF_SERVICE_OOBE_NEGOTIATOR_H_ + +#include "base/macros.h" +#include "chrome/browser/chromeos/arc/optin/arc_terms_of_service_negotiator.h" +#include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor_observer.h" + +namespace chromeos { +class ArcTermsOfServiceScreenActor; +} + +namespace arc { + +// Handles the Terms-of-service agreement user action via OOBE OptIn UI. +class ArcTermsOfServiceOobeNegotiator + : public ArcTermsOfServiceNegotiator, + public chromeos::ArcTermsOfServiceScreenActorObserver { + public: + ArcTermsOfServiceOobeNegotiator(); + ~ArcTermsOfServiceOobeNegotiator() override; + + // Injects ARC OOBE screen handler in unit tests, where OOBE UI is not + // available. + static void SetArcTermsOfServiceScreenActorForTesting( + chromeos::ArcTermsOfServiceScreenActor* actor); + + private: + // Helper to handle callbacks from + // chromeos::ArcTermsOfServiceScreenActorObserver. It removes observer from + // |screen_actor_|, resets it, and then dispatches |accepted|. It is expected + // that this method is called exactly once for each instance of + // ArcTermsOfServiceOobeNegotiator. + void HandleTermsAccepted(bool accepted); + + // chromeos::ArcTermsOfServiceScreenActorObserver: + void OnSkip() override; + void OnAccept() override; + void OnActorDestroyed(chromeos::ArcTermsOfServiceScreenActor* actor) override; + + // ArcTermsOfServiceNegotiator: + void StartNegotiationImpl() override; + + // Unowned pointer. If a user signs out while ARC OOBE opt-in is active, + // LoginDisplayHost is detached first then OnActorDestroyed is called. + // It means, in OnSkip() and OnAccept(), the Actor needs to be obtained via + // LoginDisplayHost, but in OnActorDestroyed(), the argument needs to be used. + // In order to use the same way to access the Actor, remember the pointer in + // StartNegotiationImpl(), and reset in HandleTermsAccepted(). + chromeos::ArcTermsOfServiceScreenActor* screen_actor_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(ArcTermsOfServiceOobeNegotiator); +}; + +} // namespace arc + +#endif // CHROME_BROWSER_CHROMEOS_ARC_OPTIN_ARC_TERMS_OF_SERVICE_OOBE_NEGOTIATOR_H_
diff --git a/chrome/browser/chromeos/arc/test/arc_data_removed_waiter.cc b/chrome/browser/chromeos/arc/test/arc_data_removed_waiter.cc new file mode 100644 index 0000000..d9f3acb --- /dev/null +++ b/chrome/browser/chromeos/arc/test/arc_data_removed_waiter.cc
@@ -0,0 +1,32 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "chrome/browser/chromeos/arc/test/arc_data_removed_waiter.h" + +#include "base/memory/ptr_util.h" +#include "base/run_loop.h" + +namespace arc { + +ArcDataRemovedWaiter::ArcDataRemovedWaiter() { + DCHECK(ArcSessionManager::Get()); + ArcSessionManager::Get()->AddObserver(this); +} + +ArcDataRemovedWaiter::~ArcDataRemovedWaiter() { + ArcSessionManager::Get()->RemoveObserver(this); +} + +void ArcDataRemovedWaiter::Wait() { + run_loop_ = base::MakeUnique<base::RunLoop>(); + run_loop_->Run(); + run_loop_.reset(); +} + +void ArcDataRemovedWaiter::OnArcDataRemoved() { + if (!run_loop_) + return; + run_loop_->Quit(); +} + +} // namespace arc
diff --git a/chrome/browser/chromeos/arc/test/arc_data_removed_waiter.h b/chrome/browser/chromeos/arc/test/arc_data_removed_waiter.h new file mode 100644 index 0000000..2dfd804d --- /dev/null +++ b/chrome/browser/chromeos/arc/test/arc_data_removed_waiter.h
@@ -0,0 +1,40 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_ARC_TEST_ARC_DATA_REMOVED_WAITER_H_ +#define CHROME_BROWSER_CHROMEOS_ARC_TEST_ARC_DATA_REMOVED_WAITER_H_ + +#include <memory> + +#include "base/macros.h" +#include "chrome/browser/chromeos/arc/arc_session_manager.h" + +namespace base { +class RunLoop; +} + +namespace arc { + +// Waits for ARC data has been removed. +class ArcDataRemovedWaiter : public ArcSessionManager::Observer { + public: + ArcDataRemovedWaiter(); + ~ArcDataRemovedWaiter() override; + + // Waits until ARC data is removed. Waiting is end once ArcSessionManager + // sends OnArcDataRemoved notification. + void Wait(); + + private: + // ArcSessionManager::Observer: + void OnArcDataRemoved() override; + + std::unique_ptr<base::RunLoop> run_loop_; + + DISALLOW_COPY_AND_ASSIGN(ArcDataRemovedWaiter); +}; + +} // namespace arc + +#endif // CHROME_BROWSER_CHROMEOS_ARC_TEST_ARC_DATA_REMOVED_WAITER_H_
diff --git a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.cc b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.cc index 691024f7..8fbd907a 100644 --- a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.cc +++ b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h" +#include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor.h" #include "chrome/browser/chromeos/login/screens/base_screen_delegate.h" #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/metrics/metrics_reporting_state.h" @@ -21,12 +22,12 @@ actor_(actor) { DCHECK(actor_); if (actor_) - actor_->SetDelegate(this); + actor_->AddObserver(this); } ArcTermsOfServiceScreen::~ArcTermsOfServiceScreen() { if (actor_) - actor_->SetDelegate(nullptr); + actor_->RemoveObserver(this); } void ArcTermsOfServiceScreen::Show() { @@ -43,18 +44,10 @@ } void ArcTermsOfServiceScreen::OnSkip() { - ApplyTerms(false); + Finish(BaseScreenDelegate::ARC_TERMS_OF_SERVICE_FINISHED); } void ArcTermsOfServiceScreen::OnAccept() { - ApplyTerms(true); -} - -void ArcTermsOfServiceScreen::ApplyTerms(bool accepted) { - Profile* profile = ProfileManager::GetActiveUserProfile(); - profile->GetPrefs()->SetBoolean(prefs::kArcTermsAccepted, accepted); - profile->GetPrefs()->SetBoolean(prefs::kArcEnabled, accepted); - Finish(BaseScreenDelegate::ARC_TERMS_OF_SERVICE_FINISHED); }
diff --git a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h index f2a9d92..fff5e27 100644 --- a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h +++ b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h
@@ -8,15 +8,16 @@ #include <string> #include "base/macros.h" -#include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor.h" +#include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor_observer.h" #include "chrome/browser/chromeos/login/screens/base_screen.h" namespace chromeos { +class ArcTermsOfServiceScreenActor; class BaseScreenDelegate; class ArcTermsOfServiceScreen : public BaseScreen, - public ArcTermsOfServiceScreenActor::Delegate { + public ArcTermsOfServiceScreenActorObserver { public: ArcTermsOfServiceScreen(BaseScreenDelegate* base_screen_delegate, ArcTermsOfServiceScreenActor* actor); @@ -26,14 +27,12 @@ void Show() override; void Hide() override; - // ArcTermsOfServiceScreenActor::Delegate: + // ArcTermsOfServiceScreenActorObserver: void OnSkip() override; void OnAccept() override; void OnActorDestroyed(ArcTermsOfServiceScreenActor* actor) override; private: - void ApplyTerms(bool accepted); - ArcTermsOfServiceScreenActor* actor_; DISALLOW_COPY_AND_ASSIGN(ArcTermsOfServiceScreen);
diff --git a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor.h b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor.h index af46723e..a30b60a 100644 --- a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor.h +++ b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor.h
@@ -11,34 +11,18 @@ namespace chromeos { +class ArcTermsOfServiceScreenActorObserver; + // Interface for dependency injection between TermsOfServiceScreen and its // WebUI representation. class ArcTermsOfServiceScreenActor { public: - class Delegate { - public: - virtual ~Delegate() = default; + virtual ~ArcTermsOfServiceScreenActor() = default; - // Called when the user skips the PlayStore Terms of Service. - virtual void OnSkip() = 0; - - // Called when the user accepts the PlayStore Terms of Service. - virtual void OnAccept() = 0; - - // Called when actor is destroyed so there is no dead reference to it. - virtual void OnActorDestroyed(ArcTermsOfServiceScreenActor* actor) = 0; - - protected: - Delegate() = default; - - private: - DISALLOW_COPY_AND_ASSIGN(Delegate); - }; - - virtual ~ArcTermsOfServiceScreenActor() {} - - // Sets screen this actor belongs to. - virtual void SetDelegate(Delegate* screen) = 0; + // Adds/Removes observer for actor. + virtual void AddObserver(ArcTermsOfServiceScreenActorObserver* observer) = 0; + virtual void RemoveObserver( + ArcTermsOfServiceScreenActorObserver* observer) = 0; // Shows the contents of the screen. virtual void Show() = 0;
diff --git a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor_observer.h b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor_observer.h new file mode 100644 index 0000000..cd1346ee --- /dev/null +++ b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor_observer.h
@@ -0,0 +1,36 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_ARC_TERMS_OF_SERVICE_SCREEN_ACTOR_OBSERVER_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_ARC_TERMS_OF_SERVICE_SCREEN_ACTOR_OBSERVER_H_ + +#include "base/macros.h" + +namespace chromeos { + +class ArcTermsOfServiceScreenActor; + +class ArcTermsOfServiceScreenActorObserver { + public: + virtual ~ArcTermsOfServiceScreenActorObserver() = default; + + // Called when the user skips the PlayStore Terms of Service. + virtual void OnSkip() = 0; + + // Called when the user accepts the PlayStore Terms of Service. + virtual void OnAccept() = 0; + + // Called when actor is destroyed so there is no dead reference to it. + virtual void OnActorDestroyed(ArcTermsOfServiceScreenActor* actor) = 0; + + protected: + ArcTermsOfServiceScreenActorObserver() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(ArcTermsOfServiceScreenActorObserver); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_ARC_TERMS_OF_SERVICE_SCREEN_ACTOR_OBSERVER_H_
diff --git a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc index 8cae86025..7616d25 100644 --- a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc +++ b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
@@ -469,7 +469,7 @@ } bool FakeChromeUserManager::IsCurrentUserNew() const { - return false; + return current_user_new_; } bool FakeChromeUserManager::IsCurrentUserNonCryptohomeDataEphemeral() const {
diff --git a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h index 4f38765..be415f2 100644 --- a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h +++ b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
@@ -176,6 +176,8 @@ multi_profile_user_controller_ = controller; } + void set_current_user_new(bool new_user) { current_user_new_ = new_user; } + private: // Lazily creates default user flow. UserFlow* GetDefaultUserFlow() const; @@ -186,6 +188,7 @@ std::unique_ptr<FakeSupervisedUserManager> supervised_user_manager_; AccountId owner_account_id_ = EmptyAccountId(); bool fake_ephemeral_users_enabled_ = false; + bool current_user_new_ = false; BootstrapManager* bootstrap_manager_ = nullptr; MultiProfileUserController* multi_profile_user_controller_ = nullptr;
diff --git a/chrome/browser/chromeos/options/DEPS b/chrome/browser/chromeos/options/DEPS new file mode 100644 index 0000000..9ca8436 --- /dev/null +++ b/chrome/browser/chromeos/options/DEPS
@@ -0,0 +1,6 @@ +include_rules = [ + # TODO(xdai): Remove this DEPS file after layout_delegate.h/cc is moved to a + # better place. + "!chrome/browser/ui/views/harmony/layout_delegate.h", +] +
diff --git a/chrome/browser/chromeos/options/network_config_view.cc b/chrome/browser/chromeos/options/network_config_view.cc index 9b1c9ef..4cdb6ec 100644 --- a/chrome/browser/chromeos/options/network_config_view.cc +++ b/chrome/browser/chromeos/options/network_config_view.cc
@@ -69,6 +69,7 @@ // static const int ChildNetworkConfigView::kInputFieldMinWidth = 270; +const int ChildNetworkConfigView::kInputFieldHeight = 28; NetworkConfigView::NetworkConfigView() : child_config_view_(nullptr), @@ -199,6 +200,10 @@ return child_config_view_->GetInitiallyFocusedView(); } +int NetworkConfigView::GetDefaultDialogButton() const { + return ui::DIALOG_BUTTON_CANCEL; +} + base::string16 NetworkConfigView::GetWindowTitle() const { DCHECK(!child_config_view_->GetTitle().empty()); return child_config_view_->GetTitle(); @@ -232,9 +237,12 @@ true /* show_8021x */); AddChildView(child_config_view_); // Resize the window to be able to hold the new widgets. - gfx::Size size = views::Widget::GetLocalizedContentsSize( + gfx::Size size = GetWidget()->client_view()->GetPreferredSize(); + gfx::Size predefined_size = views::Widget::GetLocalizedContentsSize( IDS_JOIN_WIFI_NETWORK_DIALOG_ADVANCED_WIDTH_CHARS, IDS_JOIN_WIFI_NETWORK_DIALOG_ADVANCED_MINIMUM_HEIGHT_LINES); + size.SetToMax(predefined_size); + // Get the new bounds with desired size at the same center point. gfx::Rect bounds = GetWidget()->GetWindowBoundsInScreen(); int horiz_padding = bounds.width() - size.width();
diff --git a/chrome/browser/chromeos/options/network_config_view.h b/chrome/browser/chromeos/options/network_config_view.h index 82fddc1..250e3d1 100644 --- a/chrome/browser/chromeos/options/network_config_view.h +++ b/chrome/browser/chromeos/options/network_config_view.h
@@ -62,6 +62,7 @@ bool Accept() override; views::View* CreateExtraView() override; views::View* GetInitiallyFocusedView() override; + int GetDefaultDialogButton() const override; // views::WidgetDelegate methods. base::string16 GetWindowTitle() const override; @@ -144,9 +145,12 @@ // Returns 'true' if the dialog is for configuration only (default is false). virtual bool IsConfigureDialog(); - // Minimum with of input fields / combo boxes. + // Minimum width of input fields / combo boxes. static const int kInputFieldMinWidth; + // The height of input fields /combo boxes. + static const int kInputFieldHeight; + protected: // Gets the default network share state for the current login state. static void GetShareStateForLoginState(bool* default_value, bool* modifiable);
diff --git a/chrome/browser/chromeos/options/wifi_config_view.cc b/chrome/browser/chromeos/options/wifi_config_view.cc index 84140764..3dc1be8 100644 --- a/chrome/browser/chromeos/options/wifi_config_view.cc +++ b/chrome/browser/chromeos/options/wifi_config_view.cc
@@ -14,6 +14,7 @@ #include "chrome/browser/chromeos/net/shill_error.h" #include "chrome/browser/chromeos/options/passphrase_textfield.h" #include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/views/harmony/layout_delegate.h" #include "chrome/grit/generated_resources.h" #include "chrome/grit/theme_resources.h" #include "chromeos/login/login_state.h" @@ -38,7 +39,6 @@ #include "ui/views/controls/label.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/layout/grid_layout.h" -#include "ui/views/layout/layout_constants.h" #include "ui/views/widget/widget.h" #include "ui/views/window/dialog_client_view.h" @@ -934,24 +934,42 @@ ParseUIProperty(&passphrase_ui_data_, network, ::onc::wifi::kPassphrase); } - views::GridLayout* layout = views::GridLayout::CreatePanel(this); - SetLayoutManager(layout); + views::GridLayout* layout = new views::GridLayout(this); + LayoutDelegate* delegate = LayoutDelegate::Get(); + layout->SetInsets(gfx::Insets( + delegate->GetLayoutDistance( + LayoutDelegate::LayoutDistanceType::PANEL_HORIZ_MARGIN), + delegate->GetLayoutDistance( + LayoutDelegate::LayoutDistanceType::PANEL_VERT_MARGIN), + delegate->GetLayoutDistance( + LayoutDelegate::LayoutDistanceType::BUTTON_VEDGE_MARGIN_NEW), + delegate->GetLayoutDistance( + LayoutDelegate::LayoutDistanceType::BUTTON_HEDGE_MARGIN_NEW))); + this->SetLayoutManager(layout); const int column_view_set_id = 0; views::ColumnSet* column_set = layout->AddColumnSet(column_view_set_id); + const int kLabelMinWidth = 150; const int kPasswordVisibleWidth = 20; // Label column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, - views::GridLayout::USE_PREF, 0, 0); - column_set->AddPaddingColumn(0, views::kRelatedControlSmallHorizontalSpacing); + views::GridLayout::USE_PREF, 0, kLabelMinWidth); + column_set->AddPaddingColumn( + 0, delegate->GetLayoutDistance(LayoutDelegate::LayoutDistanceType:: + RELATED_CONTROL_HORIZONTAL_SPACING)); // Textfield, combobox. column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, views::GridLayout::USE_PREF, 0, ChildNetworkConfigView::kInputFieldMinWidth); - column_set->AddPaddingColumn(0, views::kRelatedControlSmallHorizontalSpacing); + column_set->AddPaddingColumn( + 0, delegate->GetLayoutDistance(LayoutDelegate::LayoutDistanceType:: + RELATED_CONTROL_HORIZONTAL_SPACING)); // Password visible button / policy indicator. - column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::FILL, 1, - views::GridLayout::USE_PREF, 0, kPasswordVisibleWidth); + column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::FILL, 0, + views::GridLayout::FIXED, kPasswordVisibleWidth, 0); + column_set->AddPaddingColumn( + 0, delegate->GetLayoutDistance(LayoutDelegate::LayoutDistanceType:: + RELATED_CONTROL_HORIZONTAL_SPACING)); // SSID input if (!network || network->type() != shill::kTypeEthernet) { @@ -963,14 +981,18 @@ ssid_textfield_->set_controller(this); ssid_textfield_->SetAccessibleName(l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_NETWORK_ID)); - layout->AddView(ssid_textfield_); + layout->AddView(ssid_textfield_, 1, 1, views::GridLayout::FILL, + views::GridLayout::FILL, 0, + ChildNetworkConfigView::kInputFieldHeight); } else { views::Label* label = new views::Label(base::UTF8ToUTF16(network->name())); label->SetHorizontalAlignment(gfx::ALIGN_LEFT); layout->AddView(label); } - layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); + layout->AddPaddingRow( + 0, delegate->GetLayoutDistance(LayoutDelegate::LayoutDistanceType:: + RELATED_CONTROL_VERTICAL_SPACING)); } // Security select @@ -983,8 +1005,13 @@ security_combobox_ = new views::Combobox(security_combobox_model_.get()); security_combobox_->SetAccessibleName(label_text); security_combobox_->set_listener(this); + layout->AddView(security_combobox_, 1, 1, views::GridLayout::FILL, + views::GridLayout::FILL, 0, + ChildNetworkConfigView::kInputFieldHeight); layout->AddView(security_combobox_); - layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); + layout->AddPaddingRow( + 0, delegate->GetLayoutDistance(LayoutDelegate::LayoutDistanceType:: + RELATED_CONTROL_VERTICAL_SPACING)); } // Only enumerate certificates in the data model for 802.1X networks. @@ -1003,9 +1030,13 @@ eap_method_combobox_->SetAccessibleName(eap_label_text); eap_method_combobox_->set_listener(this); eap_method_combobox_->SetEnabled(eap_method_ui_data_.IsEditable()); - layout->AddView(eap_method_combobox_); + layout->AddView(eap_method_combobox_, 1, 1, views::GridLayout::FILL, + views::GridLayout::FILL, 0, + ChildNetworkConfigView::kInputFieldHeight); layout->AddView(new ControlledSettingIndicatorView(eap_method_ui_data_)); - layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); + layout->AddPaddingRow( + 0, delegate->GetLayoutDistance(LayoutDelegate::LayoutDistanceType:: + RELATED_CONTROL_VERTICAL_SPACING)); // Phase 2 authentication layout->StartRow(0, column_view_set_id); @@ -1021,9 +1052,13 @@ phase_2_auth_label_->SetEnabled(false); phase_2_auth_combobox_->SetEnabled(false); phase_2_auth_combobox_->set_listener(this); - layout->AddView(phase_2_auth_combobox_); + layout->AddView(phase_2_auth_combobox_, 1, 1, views::GridLayout::FILL, + views::GridLayout::FILL, 0, + ChildNetworkConfigView::kInputFieldHeight); layout->AddView(new ControlledSettingIndicatorView(phase_2_auth_ui_data_)); - layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); + layout->AddPaddingRow( + 0, delegate->GetLayoutDistance(LayoutDelegate::LayoutDistanceType:: + RELATED_CONTROL_VERTICAL_SPACING)); // Server CA certificate layout->StartRow(0, column_view_set_id); @@ -1040,10 +1075,14 @@ server_ca_cert_label_->SetEnabled(false); server_ca_cert_combobox_->SetEnabled(false); server_ca_cert_combobox_->set_listener(this); - layout->AddView(server_ca_cert_combobox_); + layout->AddView(server_ca_cert_combobox_, 1, 1, views::GridLayout::FILL, + views::GridLayout::FILL, 0, + ChildNetworkConfigView::kInputFieldHeight); layout->AddView( new ControlledSettingIndicatorView(server_ca_cert_ui_data_)); - layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); + layout->AddPaddingRow( + 0, delegate->GetLayoutDistance(LayoutDelegate::LayoutDistanceType:: + RELATED_CONTROL_VERTICAL_SPACING)); // Subject Match layout->StartRow(0, column_view_set_id); @@ -1054,8 +1093,12 @@ subject_match_textfield_ = new views::Textfield(); subject_match_textfield_->SetAccessibleName(subject_match_label_text); subject_match_textfield_->set_controller(this); - layout->AddView(subject_match_textfield_); - layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); + layout->AddView(subject_match_textfield_, 1, 1, views::GridLayout::FILL, + views::GridLayout::FILL, 0, + ChildNetworkConfigView::kInputFieldHeight); + layout->AddPaddingRow( + 0, delegate->GetLayoutDistance(LayoutDelegate::LayoutDistanceType:: + RELATED_CONTROL_VERTICAL_SPACING)); // User certificate layout->StartRow(0, column_view_set_id); @@ -1069,9 +1112,13 @@ user_cert_label_->SetEnabled(false); user_cert_combobox_->SetEnabled(false); user_cert_combobox_->set_listener(this); - layout->AddView(user_cert_combobox_); + layout->AddView(user_cert_combobox_, 1, 1, views::GridLayout::FILL, + views::GridLayout::FILL, 0, + ChildNetworkConfigView::kInputFieldHeight); layout->AddView(new ControlledSettingIndicatorView(user_cert_ui_data_)); - layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); + layout->AddPaddingRow( + 0, delegate->GetLayoutDistance(LayoutDelegate::LayoutDistanceType:: + RELATED_CONTROL_VERTICAL_SPACING)); // Identity layout->StartRow(0, column_view_set_id); @@ -1083,9 +1130,13 @@ identity_textfield_->SetAccessibleName(identity_label_text); identity_textfield_->set_controller(this); identity_textfield_->SetEnabled(identity_ui_data_.IsEditable()); - layout->AddView(identity_textfield_); + layout->AddView(identity_textfield_, 1, 1, views::GridLayout::FILL, + views::GridLayout::FILL, 0, + ChildNetworkConfigView::kInputFieldHeight); layout->AddView(new ControlledSettingIndicatorView(identity_ui_data_)); - layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); + layout->AddPaddingRow( + 0, delegate->GetLayoutDistance(LayoutDelegate::LayoutDistanceType:: + RELATED_CONTROL_VERTICAL_SPACING)); } // Passphrase input @@ -1102,7 +1153,9 @@ passphrase_textfield_->SetEnabled(network && passphrase_ui_data_.IsEditable()); passphrase_textfield_->SetAccessibleName(passphrase_label_text); - layout->AddView(passphrase_textfield_); + layout->AddView(passphrase_textfield_, 1, 1, views::GridLayout::FILL, + views::GridLayout::FILL, 0, + ChildNetworkConfigView::kInputFieldHeight); if (passphrase_ui_data_.IsManaged()) { layout->AddView(new ControlledSettingIndicatorView(passphrase_ui_data_)); @@ -1138,7 +1191,9 @@ layout->AddView(passphrase_visible_button_); } - layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); + layout->AddPaddingRow( + 0, delegate->GetLayoutDistance(LayoutDelegate::LayoutDistanceType:: + RELATED_CONTROL_VERTICAL_SPACING)); if (show_8021x) { // Anonymous identity @@ -1151,12 +1206,21 @@ identity_anonymous_label_->SetEnabled(false); identity_anonymous_textfield_->SetEnabled(false); identity_anonymous_textfield_->set_controller(this); - layout->AddView(identity_anonymous_textfield_); + layout->AddView(identity_anonymous_textfield_, 1, 1, + views::GridLayout::FILL, views::GridLayout::FILL, 0, + ChildNetworkConfigView::kInputFieldHeight); layout->AddView( new ControlledSettingIndicatorView(identity_anonymous_ui_data_)); - layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); + layout->AddPaddingRow( + 0, delegate->GetLayoutDistance(LayoutDelegate::LayoutDistanceType:: + RELATED_CONTROL_VERTICAL_SPACING)); } + // We need a little bit more padding before Checkboxes. + layout->AddPaddingRow( + 0, delegate->GetLayoutDistance(LayoutDelegate::LayoutDistanceType:: + RELATED_CONTROL_VERTICAL_SPACING)); + // Checkboxes. // Save credentials @@ -1182,7 +1246,9 @@ layout->SkipColumns(1); layout->AddView(share_network_checkbox_); } - layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); + layout->AddPaddingRow( + 0, delegate->GetLayoutDistance(LayoutDelegate::LayoutDistanceType:: + RELATED_CONTROL_VERTICAL_SPACING)); // Create an error label. layout->StartRow(0, column_view_set_id);
diff --git a/chrome/browser/extensions/api/sync_file_system/OWNERS b/chrome/browser/extensions/api/sync_file_system/OWNERS index eff33204..9d970c7 100644 --- a/chrome/browser/extensions/api/sync_file_system/OWNERS +++ b/chrome/browser/extensions/api/sync_file_system/OWNERS
@@ -1,2 +1,5 @@ kinuko@chromium.org tzik@chromium.org + +# TEAM: storage-dev@chromium.org +# COMPONENT: Blink>Storage>FileSystem
diff --git a/chrome/browser/extensions/native_bindings_apitest.cc b/chrome/browser/extensions/native_bindings_apitest.cc index ab8c043..385e453 100644 --- a/chrome/browser/extensions/native_bindings_apitest.cc +++ b/chrome/browser/extensions/native_bindings_apitest.cc
@@ -16,6 +16,11 @@ void SetUpCommandLine(base::CommandLine* command_line) override { ExtensionApiTest::SetUpCommandLine(command_line); + // We whitelist the extension so that it can use the cast.streaming.* APIs, + // which are the only APIs that are prefixed twice. + command_line->AppendSwitchASCII( + switches::kWhitelistedExtensionID, + "ddchlicdkolnonkihahngkmmmjnjlkkf"); // Note: We don't use a FeatureSwitch::ScopedOverride here because we need // the switch to be propogated to the renderer, which doesn't happen with // a ScopedOverride.
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc index c1578c2a..f5b22c7b 100644 --- a/chrome/browser/io_thread.cc +++ b/chrome/browser/io_thread.cc
@@ -28,7 +28,6 @@ #include "base/strings/utf_string_conversions.h" #include "base/threading/sequenced_worker_pool.h" #include "base/threading/thread.h" -#include "base/threading/worker_pool.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" @@ -619,9 +618,7 @@ content::CreateCookieStore(content::CookieStoreConfig()); // In-memory channel ID store. globals_->system_channel_id_service.reset( - new net::ChannelIDService( - new net::DefaultChannelIDStore(NULL), - base::WorkerPool::GetTaskRunner(true))); + new net::ChannelIDService(new net::DefaultChannelIDStore(NULL))); globals_->system_cookie_store->SetChannelIDServiceID( globals_->system_channel_id_service->GetUniqueID()); globals_->dns_probe_service.reset(new chrome_browser_net::DnsProbeService());
diff --git a/chrome/browser/media/router/presentation_service_delegate_impl.cc b/chrome/browser/media/router/presentation_service_delegate_impl.cc index e938d75..ea2dc3ab 100644 --- a/chrome/browser/media/router/presentation_service_delegate_impl.cc +++ b/chrome/browser/media/router/presentation_service_delegate_impl.cc
@@ -157,7 +157,6 @@ const MediaRoute::Id& route_id); const MediaRoute::Id GetRouteId(const std::string& presentation_id) const; - const std::vector<MediaRoute::Id> GetRouteIds() const; void OnPresentationSessionStarted( const content::PresentationSessionInfo& session, @@ -215,13 +214,6 @@ return it != presentation_id_to_route_id_.end() ? it->second : ""; } -const std::vector<MediaRoute::Id> PresentationFrame::GetRouteIds() const { - std::vector<MediaRoute::Id> route_ids; - for (const auto& e : presentation_id_to_route_id_) - route_ids.push_back(e.second); - return route_ids; -} - bool PresentationFrame::SetScreenAvailabilityListener( content::PresentationScreenAvailabilityListener* listener) { MediaSource source(GetMediaSourceFromListener(listener)); @@ -398,8 +390,6 @@ const MediaRoute::Id GetRouteId(const RenderFrameHostId& render_frame_host_id, const std::string& presentation_id) const; - const std::vector<MediaRoute::Id> GetRouteIds( - const RenderFrameHostId& render_frame_host_id) const; const PresentationRequest* default_presentation_request() const { return default_presentation_request_.get(); @@ -483,13 +473,6 @@ ? it->second->GetRouteId(presentation_id) : MediaRoute::Id(); } -const std::vector<MediaRoute::Id> PresentationFrameManager::GetRouteIds( - const RenderFrameHostId& render_frame_host_id) const { - const auto it = presentation_frames_.find(render_frame_host_id); - return it != presentation_frames_.end() ? it->second->GetRouteIds() - : std::vector<MediaRoute::Id>(); -} - bool PresentationFrameManager::SetScreenAvailabilityListener( const RenderFrameHostId& render_frame_host_id, content::PresentationScreenAvailabilityListener* listener) {
diff --git a/chrome/browser/media_galleries/fileapi/OWNERS b/chrome/browser/media_galleries/fileapi/OWNERS index 45df02a..668b552 100644 --- a/chrome/browser/media_galleries/fileapi/OWNERS +++ b/chrome/browser/media_galleries/fileapi/OWNERS
@@ -3,3 +3,4 @@ kinuko@chromium.org tzik@chromium.org +# COMPONENT: Platform>Extensions>API
diff --git a/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win_unittest.cc b/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win_unittest.cc index 7d0f923..3b97bc2 100644 --- a/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win_unittest.cc +++ b/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win_unittest.cc
@@ -111,6 +111,11 @@ } void MTPDeviceDelegateImplWinTest::TearDown() { + // The MediaFileSystemRegistry owned by the TestingBrowserProcess must be + // destroyed before the StorageMonitor because it calls + // StorageMonitor::RemoveObserver() in its destructor. + TestingBrowserProcess::DeleteInstance(); + // Windows storage monitor must be destroyed on the same thread // as construction. TestStorageMonitor::Destroy();
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc index 2fbbb3d..8dcd80c 100644 --- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc +++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -411,6 +411,11 @@ { key::kEnableMediaRouter, prefs::kEnableMediaRouter, base::Value::Type::BOOLEAN }, +#if !defined(OS_ANDROID) + { key::kShowCastIconInToolbar, + prefs::kShowCastIconInToolbar, + base::Value::Type::BOOLEAN }, +#endif // !defined(OS_ANDROID) #endif // defined(ENABLE_MEDIA_ROUTER) #if BUILDFLAG(ENABLE_WEBRTC) { key::kWebRtcUdpPortRange,
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc index 2483791..ed8760c 100644 --- a/chrome/browser/policy/policy_browsertest.cc +++ b/chrome/browser/policy/policy_browsertest.cc
@@ -224,6 +224,11 @@ #include "ui/base/window_open_disposition.h" #endif +#if defined(ENABLE_MEDIA_ROUTER) && !defined(OS_ANDROID) +#include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h" +#include "chrome/browser/ui/toolbar/media_router_action_controller.h" +#endif // defined(ENABLE_MEDIA_ROUTER) && !defined(OS_ANDROID) + using content::BrowserThread; using net::URLRequestMockHTTPJob; using testing::Mock; @@ -1067,7 +1072,7 @@ EXPECT_EQ(GURL(url::kAboutBlankURL), web_contents->GetURL()); } -IN_PROC_BROWSER_TEST_F(PolicyTest, PolicyPreprocessing) { +IN_PROC_BROWSER_TEST_F(PolicyTest, SeparateProxyPoliciesMerging) { // Add an individual proxy policy value. PolicyMap policies; policies.Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, @@ -3578,6 +3583,47 @@ IN_PROC_BROWSER_TEST_F(MediaRouterDisabledPolicyTest, MediaRouterDisabled) { EXPECT_FALSE(media_router::MediaRouterEnabled(browser()->profile())); } + +#if !defined(OS_ANDROID) +template <bool enable> +class MediaRouterActionPolicyTest : public PolicyTest { + public: + void SetUpInProcessBrowserTestFixture() override { + PolicyTest::SetUpInProcessBrowserTestFixture(); + PolicyMap policies; + policies.Set(key::kShowCastIconInToolbar, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, + base::MakeUnique<base::FundamentalValue>(enable), nullptr); + provider_.UpdateChromePolicy(policies); + } + + protected: + bool HasMediaRouterActionAtInit() const { + const std::set<std::string>& component_ids = + ComponentToolbarActionsFactory::GetInstance()->GetInitialComponentIds( + browser()->profile()); + return base::ContainsKey( + component_ids, ComponentToolbarActionsFactory::kMediaRouterActionId); + } +}; + +using MediaRouterActionEnabledPolicyTest = MediaRouterActionPolicyTest<true>; +using MediaRouterActionDisabledPolicyTest = MediaRouterActionPolicyTest<false>; + +IN_PROC_BROWSER_TEST_F(MediaRouterActionEnabledPolicyTest, + MediaRouterActionEnabled) { + EXPECT_TRUE( + MediaRouterActionController::IsActionShownByPolicy(browser()->profile())); + EXPECT_TRUE(HasMediaRouterActionAtInit()); +} + +IN_PROC_BROWSER_TEST_F(MediaRouterActionDisabledPolicyTest, + MediaRouterActionDisabled) { + EXPECT_FALSE( + MediaRouterActionController::IsActionShownByPolicy(browser()->profile())); + EXPECT_FALSE(HasMediaRouterActionAtInit()); +} +#endif // !defined(OS_ANDROID) #endif // defined(ENABLE_MEDIA_ROUTER) #if BUILDFLAG(ENABLE_WEBRTC)
diff --git a/chrome/browser/profiles/off_the_record_profile_io_data.cc b/chrome/browser/profiles/off_the_record_profile_io_data.cc index e92d4853..889665ef24 100644 --- a/chrome/browser/profiles/off_the_record_profile_io_data.cc +++ b/chrome/browser/profiles/off_the_record_profile_io_data.cc
@@ -12,7 +12,6 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/stl_util.h" -#include "base/threading/worker_pool.h" #include "build/build_config.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/custom_handlers/protocol_handler_registry.h" @@ -232,8 +231,7 @@ // For incognito, we use a non-persistent channel ID store. main_context_storage->set_channel_id_service( base::MakeUnique<net::ChannelIDService>( - new net::DefaultChannelIDStore(nullptr), - base::WorkerPool::GetTaskRunner(true))); + new net::DefaultChannelIDStore(nullptr))); using content::CookieStoreConfig; main_context_storage->set_cookie_store(CreateCookieStore(CookieStoreConfig( @@ -326,8 +324,7 @@ std::unique_ptr<net::CookieStore> cookie_store = content::CreateCookieStore(content::CookieStoreConfig()); std::unique_ptr<net::ChannelIDService> channel_id_service( - new net::ChannelIDService(new net::DefaultChannelIDStore(nullptr), - base::WorkerPool::GetTaskRunner(true))); + new net::ChannelIDService(new net::DefaultChannelIDStore(nullptr))); cookie_store->SetChannelIDServiceID(channel_id_service->GetUniqueID()); context->SetCookieStore(std::move(cookie_store));
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index 820b5472..2f493aab 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc
@@ -391,7 +391,10 @@ registry->RegisterBooleanPref(prefs::kForceEphemeralProfiles, false); #if defined(ENABLE_MEDIA_ROUTER) registry->RegisterBooleanPref(prefs::kEnableMediaRouter, true); -#endif +#if !defined(OS_ANDROID) + registry->RegisterBooleanPref(prefs::kShowCastIconInToolbar, false); +#endif // !defined(OS_ANDROID) +#endif // defined(ENABLE_MEDIA_ROUTER) // Initialize the cache prefs. registry->RegisterFilePathPref(prefs::kDiskCacheDir, base::FilePath()); registry->RegisterIntegerPref(prefs::kDiskCacheSize, 0);
diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc index 1ed4fe1..e468b2cc 100644 --- a/chrome/browser/profiles/profile_impl_io_data.cc +++ b/chrome/browser/profiles/profile_impl_io_data.cc
@@ -19,7 +19,6 @@ #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/threading/sequenced_worker_pool.h" -#include "base/threading/worker_pool.h" #include "build/build_config.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" @@ -524,8 +523,7 @@ lazy_params_->special_storage_policy.get()); main_context_storage->set_channel_id_service( base::MakeUnique<net::ChannelIDService>( - new net::DefaultChannelIDStore(channel_id_db.get()), - base::WorkerPool::GetTaskRunner(true))); + new net::DefaultChannelIDStore(channel_id_db.get()))); main_context->cookie_store()->SetChannelIDServiceID( main_context->channel_id_service()->GetUniqueID()); @@ -680,8 +678,7 @@ } std::unique_ptr<net::ChannelIDService> channel_id_service( new net::ChannelIDService( - new net::DefaultChannelIDStore(channel_id_db.get()), - base::WorkerPool::GetTaskRunner(true))); + new net::DefaultChannelIDStore(channel_id_db.get()))); cookie_store->SetChannelIDServiceID(channel_id_service->GetUniqueID()); // Build a new HttpNetworkSession that uses the new ChannelIDService. @@ -757,9 +754,9 @@ net::MEDIA_CACHE, ChooseCacheBackendType(), cache_path, cache_max_size, BrowserThread::GetTaskRunnerForThread(BrowserThread::CACHE))); - std::unique_ptr<net::HttpCache> media_http_cache = - CreateHttpFactory(main_request_context_storage()->http_network_session(), - std::move(media_backend)); + std::unique_ptr<net::HttpCache> media_http_cache = CreateHttpFactory( + main_request_context()->http_transaction_factory(), + std::move(media_backend)); // Transfer ownership of the cache to MediaRequestContext. context->SetHttpTransactionFactory(std::move(media_http_cache));
diff --git a/chrome/browser/profiles/profile_info_cache.cc b/chrome/browser/profiles/profile_info_cache.cc index fad9b363..7249a7d 100644 --- a/chrome/browser/profiles/profile_info_cache.cc +++ b/chrome/browser/profiles/profile_info_cache.cc
@@ -603,6 +603,11 @@ void ProfileInfoCache::SetAvatarIconOfProfileAtIndex(size_t index, size_t icon_index) { + if (!profiles::IsDefaultAvatarIconIndex(icon_index)) { + DLOG(WARNING) << "Unknown avatar icon index: " << icon_index; + // switch to generic avatar + icon_index = 0; + } std::unique_ptr<base::DictionaryValue> info( GetInfoForProfileAtIndex(index)->DeepCopy()); info->SetString(kAvatarIconKey,
diff --git a/chrome/browser/profiles/profile_info_cache_unittest.cc b/chrome/browser/profiles/profile_info_cache_unittest.cc index fb3e87b3..6cb72ec 100644 --- a/chrome/browser/profiles/profile_info_cache_unittest.cc +++ b/chrome/browser/profiles/profile_info_cache_unittest.cc
@@ -221,10 +221,17 @@ EXPECT_EQ(new_gaia_id, GetCache()->GetGAIAIdOfProfileAtIndex(1)); EXPECT_NE(new_user_name, GetCache()->GetUserNameOfProfileAtIndex(0)); - size_t new_icon_index = 3; + const size_t new_icon_index = 3; GetCache()->SetAvatarIconOfProfileAtIndex(1, new_icon_index); + EXPECT_EQ(new_icon_index, GetCache()->GetAvatarIconIndexOfProfileAtIndex(1)); // Not much to test. GetCache()->GetAvatarIconOfProfileAtIndex(1); + + const size_t wrong_icon_index = profiles::GetDefaultAvatarIconCount() + 1; + const size_t generic_icon_index = 0; + GetCache()->SetAvatarIconOfProfileAtIndex(1, wrong_icon_index); + EXPECT_EQ(generic_icon_index, + GetCache()->GetAvatarIconIndexOfProfileAtIndex(1)); } TEST_F(ProfileInfoCacheTest, Sort) {
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc index 0e6c1cc..40ede50 100644 --- a/chrome/browser/profiles/profile_io_data.cc +++ b/chrome/browser/profiles/profile_io_data.cc
@@ -1308,8 +1308,10 @@ } std::unique_ptr<net::HttpCache> ProfileIOData::CreateHttpFactory( - net::HttpNetworkSession* shared_session, + net::HttpTransactionFactory* main_http_factory, std::unique_ptr<net::HttpCache::BackendFactory> backend) const { + DCHECK(main_http_factory); + net::HttpNetworkSession* shared_session = main_http_factory->GetSession(); return base::MakeUnique<net::HttpCache>( base::WrapUnique(new DevToolsNetworkTransactionFactory( network_controller_handle_.GetController(), shared_session)),
diff --git a/chrome/browser/profiles/profile_io_data.h b/chrome/browser/profiles/profile_io_data.h index 4624915..dd4b25d 100644 --- a/chrome/browser/profiles/profile_io_data.h +++ b/chrome/browser/profiles/profile_io_data.h
@@ -415,9 +415,10 @@ net::HttpNetworkSession* session, std::unique_ptr<net::HttpCache::BackendFactory> main_backend) const; - // Creates network transaction factory. + // Creates network transaction factory. The created factory will share + // HttpNetworkSession with |main_http_factory|. std::unique_ptr<net::HttpCache> CreateHttpFactory( - net::HttpNetworkSession* shared_session, + net::HttpTransactionFactory* main_http_factory, std::unique_ptr<net::HttpCache::BackendFactory> backend) const; void SetCookieSettingsForTesting(
diff --git a/chrome/browser/resources/.clang-format b/chrome/browser/resources/.clang-format new file mode 100644 index 0000000..d455a85 --- /dev/null +++ b/chrome/browser/resources/.clang-format
@@ -0,0 +1,8 @@ +# Please keep this file the same as ui/webui/resources/.clang-format. +BasedOnStyle: Chromium + +# Renaming quotes in <include> and <if> break things. +# For normal JS code, please prefer ' to ". +JavaScriptQuotes: Leave + +AllowShortFunctionsOnASingleLine: Empty
diff --git a/chrome/browser/resources/md_history/app.vulcanized.html b/chrome/browser/resources/md_history/app.vulcanized.html index f8800fc..b636efe 100644 --- a/chrome/browser/resources/md_history/app.vulcanized.html +++ b/chrome/browser/resources/md_history/app.vulcanized.html
@@ -3514,18 +3514,15 @@ } iron-selector > a { - font-family: var(--paper-font-subhead_-_font-family); -webkit-font-smoothing: var(--paper-font-subhead_-_-webkit-font-smoothing); font-size: var(--paper-font-subhead_-_font-size); font-weight: var(--paper-font-subhead_-_font-weight); line-height: var(--paper-font-subhead_-_line-height); - - -webkit-margin-end: 4px; + -webkit-margin-end: 4px; -webkit-padding-start: 24px; align-items: center; box-sizing: border-box; color: inherit; cursor: pointer; display: flex; - font-size: 108%; font-weight: 500; - min-height: 48px; + min-height: 40px; position: relative; text-decoration: none; }
diff --git a/chrome/browser/resources/md_history/side_bar.html b/chrome/browser/resources/md_history/side_bar.html index 373009e2..79a5180 100644 --- a/chrome/browser/resources/md_history/side_bar.html +++ b/chrome/browser/resources/md_history/side_bar.html
@@ -49,7 +49,6 @@ } iron-selector > a { - @apply(--paper-font-subhead); /* Ensure the focus outline appears correctly (crbug.com/655503). */ -webkit-margin-end: 4px; -webkit-padding-start: 24px; @@ -58,9 +57,8 @@ color: inherit; cursor: pointer; display: flex; - font-size: 108%; font-weight: 500; - min-height: 48px; + min-height: 40px; position: relative; text-decoration: none; }
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.html b/chrome/browser/resources/settings/basic_page/basic_page.html index 8b59da3d..51554a1e 100644 --- a/chrome/browser/resources/settings/basic_page/basic_page.html +++ b/chrome/browser/resources/settings/basic_page/basic_page.html
@@ -81,6 +81,14 @@ </settings-internet-page> </settings-section> </template> + <template is="dom-if" if="[[showPage(pageVisibility.bluetooth)]]" + restamp> + <settings-section page-title="$i18n{bluetoothPageTitle}" + section="bluetooth"> + <settings-bluetooth-page prefs="{{prefs}}"> + </settings-bluetooth-page> + </settings-section> + </template> </if> <template is="dom-if" if="[[showPage(pageVisibility.people)]]" restamp> <settings-section page-title="$i18n{peoplePageTitle}" @@ -179,16 +187,6 @@ </settings-privacy-page> </settings-section> </template> -<if expr="chromeos"> - <template is="dom-if" if="[[showPage(pageVisibility.bluetooth)]]" - restamp> - <settings-section page-title="$i18n{bluetoothPageTitle}" - section="bluetooth"> - <settings-bluetooth-page prefs="{{prefs}}"> - </settings-bluetooth-page> - </settings-section> - </template> -</if> <template is="dom-if" if="[[showPage(pageVisibility.passwordsAndForms)]]" restamp> <settings-section
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_device_dialog.html b/chrome/browser/resources/settings/bluetooth_page/bluetooth_device_dialog.html index b4a00c2..94f0df2 100644 --- a/chrome/browser/resources/settings/bluetooth_page/bluetooth_device_dialog.html +++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_device_dialog.html
@@ -74,7 +74,8 @@ margin: 0 20px; } </style> - <dialog is="cr-dialog" id="dialog" on-cancel="onDialogCanceled_"> + <dialog is="cr-dialog" id="dialog" on-cancel="onDialogCanceled_" + on-closed="onDialogCanceled_"> <div class="title">$i18n{bluetoothPairDevicePageTitle}</div> <div class="body"> <div class="contents layout vertical center center-justified"> @@ -103,27 +104,27 @@ <div id="pairing" class="settings-box first layout vertical center center-justified"> <div class="dialog-message"> - [[getMessage_(pairingDevice, pairingEvent)]] + [[getMessage_(pairingDevice, pairingEvent_)]] </div> - <div hidden$="[[!showEnterPincode_(pairingEvent)]]"> + <div hidden$="[[!showEnterPincode_(pairingEvent_)]]"> <paper-input id="pincode" minlength="1" maxlength="16" type="text" auto-validate value="{{pinOrPass}}"> </paper-input> </div> - <div hidden$="[[!showEnterPasskey_(pairingEvent)]]"> + <div hidden$="[[!showEnterPasskey_(pairingEvent_)]]"> <paper-input id="passkey" minlength="6" maxlength="6" type="text" auto-validate value="{{pinOrPass}}"> </paper-input> </div> <div id="pinDiv" class="layout horizontal center center-justified" - hidden="[[!showDisplayPassOrPin_(pairingEvent)]]"> + hidden="[[!showDisplayPassOrPin_(pairingEvent_)]]"> <template is="dom-repeat" items="[[digits]]"> - <span class$="[[getPinClass_(index, pairingEvent)]]"> - [[getPinDigit_(index, pairingEvent)]] + <span class$="[[getPinClass_(index, pairingEvent_)]]"> + [[getPinDigit_(index, pairingEvent_)]] </span> </template> - <span class$="[[getPinClass_(-1, pairingEvent)]]" - hidden="[[showAcceptReject_(pairingEvent)]]"> + <span class$="[[getPinClass_(-1, pairingEvent_)]]" + hidden="[[showAcceptReject_(pairingEvent_)]]"> $i18n{bluetoothEnterKey} </span> </div> @@ -151,16 +152,17 @@ </paper-button> </template> <template is="dom-if" if="[[isDialogType_('pairDevice', dialogId)]]"> - <paper-button hidden$="[[!showAcceptReject_(pairingEvent)]]" + <paper-button hidden$="[[!showAcceptReject_(pairingEvent_)]]" on-tap="onAcceptTap_">$i18n{bluetoothAccept}</paper-button> - <paper-button hidden$="[[!showAcceptReject_(pairingEvent)]]" + <paper-button hidden$="[[!showAcceptReject_(pairingEvent_)]]" on-tap="onRejectTap_">$i18n{bluetoothReject}</paper-button> - <paper-button hidden$="[[!showConnect_(pairingEvent)]]" - disabled="[[!enableConnect_(pairingEvent, pinOrPass)]]" + <paper-button hidden$="[[!showConnect_(pairingEvent_)]]" + disabled="[[!enableConnect_(pairingEvent_, pinOrPass)]]" on-tap="onConnectTap_">$i18n{bluetoothPair}</paper-button> - <paper-button hidden$="[[!showDismiss_(pairingDevice, pairingEvent)]]" + <paper-button + hidden$="[[!showDismiss_(pairingDevice, pairingEvent_)]]" on-tap="close">$i18n{bluetoothDismiss}</paper-button> - <paper-button hidden$="[[showDismiss_(pairingDevice, pairingEvent)]]" + <paper-button hidden$="[[showDismiss_(pairingDevice, pairingEvent_)]]" on-tap="onCancelTap_"> $i18n{cancel} </paper-button>
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_device_dialog.js b/chrome/browser/resources/settings/bluetooth_page/bluetooth_device_dialog.js index a5ebfd0..6e50fb4e 100644 --- a/chrome/browser/resources/settings/bluetooth_page/bluetooth_device_dialog.js +++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_device_dialog.js
@@ -4,8 +4,6 @@ cr.exportPath('settings'); -(function() { - var PairingEventType = chrome.bluetoothPrivate.PairingEventType; // NOTE(dbeam): even though these behaviors are only used privately, they must @@ -45,7 +43,8 @@ }, }, - /** @type {boolean} */ itemWasFocused_: false, + /** @type {boolean} */ + itemWasFocused_: false, /** @private */ adapterStateChanged_: function() { @@ -67,7 +66,9 @@ return; } // Otherwise try again. - setTimeout(function() { this.deviceListChanged_(); }.bind(this), 100); + setTimeout(function() { + this.deviceListChanged_(); + }.bind(this), 100); }, /** @private */ @@ -100,20 +101,33 @@ properties: { /** * Current Pairing device. - * @type {?chrome.bluetooth.Device|undefined} + * @type {!chrome.bluetooth.Device|undefined} */ pairingDevice: Object, /** * Current Pairing event. - * @type {?chrome.bluetoothPrivate.PairingEvent|undefined} + * @type {?chrome.bluetoothPrivate.PairingEvent} */ - pairingEvent: Object, + pairingEvent_: { + type: Object, + value: null, + }, /** Pincode or passkey value, used to trigger connect enabled changes. */ pinOrPass: String, /** + * Interface for bluetoothPrivate calls. Set in bluetooth-page. + * @type {BluetoothPrivate} + * @private + */ + bluetoothPrivate: { + type: Object, + value: chrome.bluetoothPrivate, + }, + + /** * @const * @type {!Array<number>} */ @@ -125,13 +139,59 @@ }, observers: [ - 'pairingChanged_(pairingDevice, pairingEvent)', + 'pairingChanged_(pairingDevice, pairingEvent_)', ], + /** + * Listener for chrome.bluetoothPrivate.onPairing events. + * @type {?function(!chrome.bluetoothPrivate.PairingEvent)} + * @private + */ + bluetoothPrivateOnPairingListener_: null, + + /** Called when the dialog is opened. Starts listening for pairing events. */ + startPairing: function() { + if (!this.bluetoothPrivateOnPairingListener_) { + this.bluetoothPrivateOnPairingListener_ = + this.onBluetoothPrivateOnPairing_.bind(this); + this.bluetoothPrivate.onPairing.addListener( + this.bluetoothPrivateOnPairingListener_); + } + }, + + /** Called when the dialog is closed. */ + endPairing: function() { + if (this.bluetoothPrivateOnPairingListener_) { + this.bluetoothPrivate.onPairing.removeListener( + this.bluetoothPrivateOnPairingListener_); + this.bluetoothPrivateOnPairingListener_ = null; + } + this.pairingEvent_ = null; + }, + + /** + * Process bluetoothPrivate.onPairing events. + * @param {!chrome.bluetoothPrivate.PairingEvent} event + * @private + */ + onBluetoothPrivateOnPairing_: function(event) { + if (!this.pairingDevice || + event.device.address != this.pairingDevice.address) { + return; + } + if (event.pairing == PairingEventType.KEYS_ENTERED && + event.passkey === undefined && this.pairingEvent_) { + // 'keysEntered' event might not include the updated passkey so preserve + // the current one. + event.passkey = this.pairingEvent_.passkey; + } + this.pairingEvent_ = event; + }, + /** @private */ pairingChanged_: function() { // Auto-close the dialog when pairing completes. - if (this.pairingDevice && this.pairingDevice.connected) { + if (this.pairingDevice.connected) { this.close(); return; } @@ -143,13 +203,11 @@ * @private */ getMessage_: function() { - if (!this.pairingDevice) - return ''; var message; - if (!this.pairingEvent) + if (!this.pairingEvent_) message = 'bluetoothStartConnecting'; else - message = this.getEventDesc_(this.pairingEvent.pairing); + message = this.getEventDesc_(this.pairingEvent_.pairing); return this.i18n(message, this.pairingDevice.name); }, @@ -158,8 +216,8 @@ * @private */ showEnterPincode_: function() { - return !!this.pairingEvent && - this.pairingEvent.pairing == PairingEventType.REQUEST_PINCODE; + return !!this.pairingEvent_ && + this.pairingEvent_.pairing == PairingEventType.REQUEST_PINCODE; }, /** @@ -167,8 +225,8 @@ * @private */ showEnterPasskey_: function() { - return !!this.pairingEvent && - this.pairingEvent.pairing == PairingEventType.REQUEST_PASSKEY; + return !!this.pairingEvent_ && + this.pairingEvent_.pairing == PairingEventType.REQUEST_PASSKEY; }, /** @@ -176,9 +234,9 @@ * @private */ showDisplayPassOrPin_: function() { - if (!this.pairingEvent) + if (!this.pairingEvent_) return false; - var pairing = this.pairingEvent.pairing; + var pairing = this.pairingEvent_.pairing; return ( pairing == PairingEventType.DISPLAY_PINCODE || pairing == PairingEventType.DISPLAY_PASSKEY || @@ -191,8 +249,8 @@ * @private */ showAcceptReject_: function() { - return !!this.pairingEvent && - this.pairingEvent.pairing == PairingEventType.CONFIRM_PASSKEY; + return !!this.pairingEvent_ && + this.pairingEvent_.pairing == PairingEventType.CONFIRM_PASSKEY; }, /** @@ -200,9 +258,9 @@ * @private */ showConnect_: function() { - if (!this.pairingEvent) + if (!this.pairingEvent_) return false; - var pairing = this.pairingEvent.pairing; + var pairing = this.pairingEvent_.pairing; return pairing == PairingEventType.REQUEST_PINCODE || pairing == PairingEventType.REQUEST_PASSKEY; }, @@ -215,7 +273,7 @@ if (!this.showConnect_()) return false; var inputId = - (this.pairingEvent.pairing == PairingEventType.REQUEST_PINCODE) ? + (this.pairingEvent_.pairing == PairingEventType.REQUEST_PINCODE) ? '#pincode' : '#passkey'; var paperInput = /** @type {!PaperInputElement} */ (this.$$(inputId)); @@ -229,9 +287,9 @@ * @private */ showDismiss_: function() { - return (!!this.paringDevice && this.pairingDevice.paired) || - (!!this.pairingEvent && - this.pairingEvent.pairing == PairingEventType.COMPLETE); + return this.pairingDevice.paired || + (!!this.pairingEvent_ && + this.pairingEvent_.pairing == PairingEventType.COMPLETE); }, /** @private */ @@ -262,12 +320,23 @@ response: response }; if (response == chrome.bluetoothPrivate.PairingResponse.CONFIRM) { - var pairing = this.pairingEvent.pairing; + var pairing = this.pairingEvent_.pairing; if (pairing == PairingEventType.REQUEST_PINCODE) options.pincode = this.$$('#pincode').value; else if (pairing == PairingEventType.REQUEST_PASSKEY) options.passkey = parseInt(this.$$('#passkey').value, 10); } + this.bluetoothPrivate.setPairingResponse(options, function() { + if (chrome.runtime.lastError) { + // TODO(stevenjb): Show error. + console.error( + 'Error setting pairing response: ' + options.device.name + + ': Response: ' + options.response + + ': Error: ' + chrome.runtime.lastError.message); + } + this.close(); + }.bind(this)); + this.fire('response', options); }, @@ -292,19 +361,20 @@ * @private */ getPinDigit_: function(index) { - if (!this.pairingEvent) + if (!this.pairingEvent_) return ''; var digit = '0'; - var pairing = this.pairingEvent.pairing; + var pairing = this.pairingEvent_.pairing; if (pairing == PairingEventType.DISPLAY_PINCODE && - this.pairingEvent.pincode && index < this.pairingEvent.pincode.length) { - digit = this.pairingEvent.pincode[index]; + this.pairingEvent_.pincode && + index < this.pairingEvent_.pincode.length) { + digit = this.pairingEvent_.pincode[index]; } else if ( - this.pairingEvent.passkey && + this.pairingEvent_.passkey && (pairing == PairingEventType.DISPLAY_PASSKEY || pairing == PairingEventType.KEYS_ENTERED || pairing == PairingEventType.CONFIRM_PASSKEY)) { - var passkeyString = String(this.pairingEvent.passkey); + var passkeyString = String(this.pairingEvent_.passkey); if (index < passkeyString.length) digit = passkeyString[index]; } @@ -317,21 +387,21 @@ * @private */ getPinClass_: function(index) { - if (!this.pairingEvent) + if (!this.pairingEvent_) return ''; - if (this.pairingEvent.pairing == PairingEventType.CONFIRM_PASSKEY) + if (this.pairingEvent_.pairing == PairingEventType.CONFIRM_PASSKEY) return 'confirm'; var cssClass = 'display'; - if (this.pairingEvent.pairing == PairingEventType.DISPLAY_PASSKEY) { + if (this.pairingEvent_.pairing == PairingEventType.DISPLAY_PASSKEY) { if (index == 0) cssClass += ' next'; else cssClass += ' untyped'; } else if ( - this.pairingEvent.pairing == PairingEventType.KEYS_ENTERED && - this.pairingEvent.enteredKey) { - var enteredKey = this.pairingEvent.enteredKey; // 1-7 - var lastKey = this.digits.length; // 6 + this.pairingEvent_.pairing == PairingEventType.KEYS_ENTERED && + this.pairingEvent_.enteredKey) { + var enteredKey = this.pairingEvent_.enteredKey; // 1-7 + var lastKey = this.digits.length; // 6 if ((index == -1 && enteredKey > lastKey) || (index + 1 == enteredKey)) cssClass += ' next'; else if (index > enteredKey) @@ -360,16 +430,18 @@ }, observers: [ - 'dialogUpdated_(dialogId, pairingEvent)', + 'dialogUpdated_(dialogId, pairingEvent_)', ], open: function() { + this.startPairing(); this.pinOrPass = ''; this.getDialog_().showModal(); this.itemWasFocused_ = false; }, close: function() { + this.endPairing(); var dialog = this.getDialog_(); if (dialog.open) dialog.close(); @@ -409,7 +481,6 @@ onDialogCanceled_: function() { if (this.dialogId == 'pairDevice') this.sendResponse_(chrome.bluetoothPrivate.PairingResponse.CANCEL); + this.endPairing(); }, }); - -})();
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html b/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html index 20fd975..74de61b 100644 --- a/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html +++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html
@@ -1,109 +1,50 @@ -<link rel="import" href="chrome://resources/cr_elements/cr_scrollable_behavior.html"> <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/classes/iron-flex-layout.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html"> <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="/icons.html"> +<link rel="import" href="/prefs/prefs.html"> <link rel="import" href="/settings_page/settings_animated_pages.html"> +<link rel="import" href="/settings_page/settings_subpage.html"> <link rel="import" href="/settings_shared_css.html"> -<link rel="import" href="bluetooth_device_dialog.html"> -<link rel="import" href="bluetooth_device_list_item.html"> +<link rel="import" href="bluetooth_subpage.html"> <dom-module id="settings-bluetooth-page"> <template> <style include="settings-shared"> - cr-expand-button { - -webkit-margin-end: 10px; - } - - #deviceList { - max-height: 300px; - overflow-y: auto; - } - - .no-devices { - min-height: var(--settings-row-min-height); - } - - settings-bluetooth-add-device-dialog, - settings-bluetooth-pair-device-dialog { - height: 400px; - padding: 0; - width: 500px; - } </style> <settings-animated-pages id="pages" section="bluetooth"> <neon-animatable route-path="default"> - <div class="settings-box first" actionable - on-tap="toggleDeviceListExpanded_"> - <iron-icon icon="[[getIcon_(bluetoothEnabled_, deviceConnected_)]]"> - </iron-icon> + <div class="settings-box first" actionable on-tap="onTap_"> + <iron-icon icon="[[getIcon_(bluetoothEnabled_)]]"></iron-icon> <span class="middle">[[getTitle_(bluetoothEnabled_)]]</span> <cr-policy-pref-indicator pref="[[prefs.cros.device.allow_bluetooth]]" hidden="[[prefs.cros.device.allow_bluetooth.value]]"> </cr-policy-pref-indicator> - <cr-expand-button id="expandListButton" - alt="$i18n{bluetoothExpandA11yLabel}" - hidden$="[[!bluetoothEnabled_]]" - expanded="{{deviceListExpanded_}}"> - </cr-expand-button> - <div class="secondary-action"> - <paper-toggle-button id="enableBluetooth" - checked="{{bluetoothEnabled_}}" - disabled="[[!adapterState_.available]]" - on-change="onBluetoothEnabledChange_" on-tap="stopTap_"> - </paper-toggle-button> - </div> + <button class="subpage-arrow" is="paper-icon-button-light" + on-tap="onSubpageArrowTap_"> + </button> </div> - <iron-collapse opened="[[canShowDeviceList_(bluetoothEnabled_, - deviceListExpanded_)]]"> - <div id="deviceList" class="list-frame vertical-list" - on-device-event="onDeviceEvent_"> - <div id="container" class="layout vertical" scrollable> - <iron-list items="[[getPairedOrConnecting_(deviceList_.*)]]" - selection-enabled selected-item="{{selectedItem_}}" - scroll-target="container"> - <template> - <bluetooth-device-list-item device="[[item]]" - tabindex$="[[tabIndex]]"> - </bluetooth-device-list-item> - </template> - </iron-list> - </div> - <div class="no-devices layout horizontal center" - hidden$="[[haveDevices_(deviceList_.*)]]"> - $i18n{bluetoothNoDevices} - </div> - </div> - <div class="settings-box" hidden$="[[!bluetoothEnabled_]]"> - <paper-button class="primary-button" on-tap="onAddDeviceTap_"> - $i18n{bluetoothPairDevice} - </paper-button> - </div> - </iron-collapse> </neon-animatable> + + <template is="dom-if" route-path="/bluetoothDevices"> + <settings-subpage associated-control="[[$$('#bluetoothDevices')]]" + page-title="$i18n{bluetoothPageTitle}" + show-spinner="[[showSpinner_]]"> + <settings-bluetooth-subpage + adapter-state="[[adapterState_]]" + bluetooth-enabled="{{bluetoothEnabled_}}" + bluetooth="[[bluetooth]]" + bluetooth-private="[[bluetoothPrivate]]" + show-spinner="{{showSpinner_}}"> + </settings-bluetooth-subpage> + </settings-subpage> + </template> + </settings-animated-pages> - - <template is="dom-if" if="[[deviceListExpanded_]]"> - <bluetooth-device-dialog id="deviceDialog" - adapter-state="[[adapterState_]]" - device-list="[[deviceList_]]" - dialog-id="[[dialogId_]]" - on-close="onDialogClosed_" - error-message="[[errorMessage_]]" - on-device-event="onDeviceEvent_" - on-response="onResponse_" - pairing-device="[[pairingDevice_]]" - pairing-event="[[pairingEvent_]]"> - </bluetooth-device-dialog> - </template> - </template> <script src="bluetooth_page.js"></script> </dom-module>
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js b/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js index 66db1a9..f7759a3 100644 --- a/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js +++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js
@@ -4,18 +4,11 @@ /** * @fileoverview - * 'settings-bluetooth-page' is the settings page for managing bluetooth - * properties and devices. - * - * Example: - * <core-animated-pages> - * <settings-bluetooth-page> - * </settings-bluetooth-page> - * ... other pages ... - * </core-animated-pages> + * 'Settings page for managing bluetooth properties and devices. This page + * just provodes a summary and link to the subpage. */ -var bluetoothPage = bluetoothPage || { +var bluetoothApis = bluetoothApis || { /** * Set this to provide a fake implementation for testing. * @type {Bluetooth} @@ -32,7 +25,7 @@ Polymer({ is: 'settings-bluetooth-page', - behaviors: [I18nBehavior, CrScrollableBehavior], + behaviors: [I18nBehavior], properties: { /** Preferences state. */ @@ -44,13 +37,17 @@ /** @private */ bluetoothEnabled_: { type: Boolean, - value: false, observer: 'bluetoothEnabledChanged_', + notify: true, }, - /** @private */ - deviceListExpanded_: { + /** + * Set by bluetooth-subpage to controll spinner visibilit in the header. + * @private + */ + showSpinner_: { type: Boolean, + notify: true, value: false, }, @@ -59,67 +56,12 @@ * @type {!chrome.bluetooth.AdapterState|undefined} * @private */ - adapterState_: Object, - - /** - * Whether or not a bluetooth device is connected. - * @private - */ - deviceConnected_: Boolean, - - /** - * The ordered list of bluetooth devices. - * @type {!Array<!chrome.bluetooth.Device>} - * @private - */ - deviceList_: { - type: Array, - value: function() { - return []; - }, - }, - - /** - * Reflects the iron-list selecteditem property. - * @type {!chrome.bluetooth.Device} - * @private - */ - selectedItem_: { + adapterState_: { type: Object, - observer: 'selectedItemChanged_', + notify: true, }, /** - * Set to the name of the dialog to show. This page uses a single - * paper-dialog to host one of three dialog elements, 'addDevice', - * 'pairDevice', or 'connectError'. This allows a seamless transition - * between dialogs. Note: This property should be set before opening the - * dialog and setting the property will not itself cause the dialog to open. - * @private - */ - dialogId_: String, - - /** - * Current Pairing device. - * @type {?chrome.bluetooth.Device|undefined} - * @private - */ - pairingDevice_: Object, - - /** - * Current Pairing event. - * @type {?chrome.bluetoothPrivate.PairingEvent|undefined} - * @private - */ - pairingEvent_: Object, - - /** - * The translated error message to show when a connect error occurs. - * @private - */ - errorMessage_: String, - - /** * Interface for bluetooth calls. May be overriden by tests. * @type {Bluetooth} * @private @@ -149,33 +91,12 @@ */ bluetoothAdapterStateChangedListener_: undefined, - /** - * Listener for chrome.bluetooth.onBluetoothDeviceAdded/Changed events. - * @type {function(!chrome.bluetooth.Device)|undefined} - * @private - */ - bluetoothDeviceUpdatedListener_: undefined, - - /** - * Listener for chrome.bluetooth.onBluetoothDeviceRemoved events. - * @type {function(!chrome.bluetooth.Device)|undefined} - * @private - */ - bluetoothDeviceRemovedListener_: undefined, - - /** - * Listener for chrome.bluetoothPrivate.onPairing events. - * @type {function(!chrome.bluetoothPrivate.PairingEvent)|undefined} - * @private - */ - bluetoothPrivateOnPairingListener_: undefined, - /** @override */ ready: function() { - if (bluetoothPage.bluetoothApiForTest) - this.bluetooth = bluetoothPage.bluetoothApiForTest; - if (bluetoothPage.bluetoothPrivateApiForTest) - this.bluetoothPrivate = bluetoothPage.bluetoothPrivateApiForTest; + if (bluetoothApis.bluetoothApiForTest) + this.bluetooth = bluetoothApis.bluetoothApiForTest; + if (bluetoothApis.bluetoothPrivateApiForTest) + this.bluetoothPrivate = bluetoothApis.bluetoothPrivateApiForTest; }, /** @override */ @@ -185,18 +106,6 @@ this.bluetooth.onAdapterStateChanged.addListener( this.bluetoothAdapterStateChangedListener_); - this.bluetoothDeviceUpdatedListener_ = - this.onBluetoothDeviceUpdated_.bind(this); - this.bluetooth.onDeviceAdded.addListener( - this.bluetoothDeviceUpdatedListener_); - this.bluetooth.onDeviceChanged.addListener( - this.bluetoothDeviceUpdatedListener_); - - this.bluetoothDeviceRemovedListener_ = - this.onBluetoothDeviceRemoved_.bind(this); - this.bluetooth.onDeviceRemoved.addListener( - this.bluetoothDeviceRemovedListener_); - // Request the inital adapter state. this.bluetooth.getAdapterState(this.bluetoothAdapterStateChangedListener_); }, @@ -207,40 +116,6 @@ this.bluetooth.onAdapterStateChanged.removeListener( this.bluetoothAdapterStateChangedListener_); } - if (this.bluetoothDeviceUpdatedListener_) { - this.bluetooth.onDeviceAdded.removeListener( - this.bluetoothDeviceUpdatedListener_); - this.bluetooth.onDeviceChanged.removeListener( - this.bluetoothDeviceUpdatedListener_); - } - if (this.bluetoothDeviceRemovedListener_) { - this.bluetooth.onDeviceRemoved.removeListener( - this.bluetoothDeviceRemovedListener_); - } - }, - - /** @private */ - bluetoothEnabledChanged_: function() { - // When bluetooth is enabled, auto-expand the device list. - if (this.bluetoothEnabled_) - this.deviceListExpanded_ = true; - }, - - /** @private */ - deviceListChanged_: function() { - for (var i = 0; i < this.deviceList_.length; ++i) { - if (this.deviceList_[i].connected) { - this.deviceConnected_ = true; - return; - } - } - this.deviceConnected_ = false; - }, - - /** @private */ - selectedItemChanged_: function() { - if (this.selectedItem_) - this.connectDevice_(this.selectedItem_); }, /** @@ -250,8 +125,6 @@ getIcon_: function() { if (!this.bluetoothEnabled_) return 'settings:bluetooth-disabled'; - if (this.deviceConnected_) - return 'settings:bluetooth-connected'; return 'settings:bluetooth'; }, @@ -264,40 +137,37 @@ this.bluetoothEnabled_ ? 'bluetoothEnabled' : 'bluetoothDisabled'); }, + /** + * Process bluetooth.onAdapterStateChanged events. + * @param {!chrome.bluetooth.AdapterState} state + * @private + */ + onBluetoothAdapterStateChanged_: function(state) { + this.adapterState_ = state; + this.bluetoothEnabled_ = state.powered; + }, + /** @private */ - toggleDeviceListExpanded_: function() { - this.deviceListExpanded_ = !this.deviceListExpanded_; - }, - - /** - * @return {boolean} Whether the <iron-collapse> can be shown. - * @private - */ - canShowDeviceList_: function() { - return this.bluetoothEnabled_ && this.deviceListExpanded_; - }, - - /** - * If bluetooth is enabled, request the complete list of devices and update - * this.deviceList_. - * @private - */ - updateDeviceList_: function() { - if (!this.bluetoothEnabled_) { - this.deviceList_ = []; + onTap_: function() { + if (this.adapterState_.available === false) return; - } - this.bluetooth.getDevices(function(devices) { - this.deviceList_ = devices; - this.updateScrollableContents(); - }.bind(this)); + if (!this.bluetoothEnabled_) + this.bluetoothEnabled_ = true; + else + this.openSubpage_(); }, /** - * Event called when a user action changes the bluetoothEnabled state. + * @param {Event} e * @private */ - onBluetoothEnabledChange_: function() { + onSubpageArrowTap_: function(e) { + this.openSubpage_(); + e.stopPropagation(); + }, + + /** @private */ + bluetoothEnabledChanged_: function() { this.bluetoothPrivate.setAdapterState( {powered: this.bluetoothEnabled_}, function() { if (chrome.runtime.lastError) { @@ -308,320 +178,8 @@ }); }, - /** - * Process bluetooth.onAdapterStateChanged events. - * @param {!chrome.bluetooth.AdapterState} state - * @private - */ - onBluetoothAdapterStateChanged_: function(state) { - this.adapterState_ = state; - this.bluetoothEnabled_ = state.powered; - this.updateDeviceList_(); - }, - - /** - * Process bluetooth.onDeviceAdded and onDeviceChanged events. - * @param {!chrome.bluetooth.Device} device - * @private - */ - onBluetoothDeviceUpdated_: function(device) { - var address = device.address; - if (this.dialogId_ && this.pairingDevice_ && - this.pairingDevice_.address == address) { - this.pairingDevice_ = device; - } - var index = this.getDeviceIndex_(address); - if (index >= 0) { - this.set('deviceList_.' + index, device); - return; - } - this.push('deviceList_', device); - }, - - /** - * Process bluetooth.onDeviceRemoved events. - * @param {!chrome.bluetooth.Device} device - * @private - */ - onBluetoothDeviceRemoved_: function(device) { - var address = device.address; - var index = this.getDeviceIndex_(address); - if (index < 0) - return; - this.splice('deviceList_', index, 1); - }, - /** @private */ - startDiscovery_: function() { - if (!this.adapterState_ || this.adapterState_.discovering) - return; - - if (!this.bluetoothPrivateOnPairingListener_) { - this.bluetoothPrivateOnPairingListener_ = - this.onBluetoothPrivateOnPairing_.bind(this); - this.bluetoothPrivate.onPairing.addListener( - this.bluetoothPrivateOnPairingListener_); - } - - this.bluetooth.startDiscovery(function() { - if (chrome.runtime.lastError) { - if (chrome.runtime.lastError.message == 'Failed to stop discovery') { - // May happen if also started elsewhere; ignore. - return; - } - console.error( - 'startDiscovery Error: ' + chrome.runtime.lastError.message); - } - }); - }, - - /** @private */ - stopDiscovery_: function() { - if (!this.get('adapterState_.discovering')) - return; - - if (this.bluetoothPrivateOnPairingListener_) { - this.bluetoothPrivate.onPairing.removeListener( - this.bluetoothPrivateOnPairingListener_); - this.bluetoothPrivateOnPairingListener_ = undefined; - } - - this.bluetooth.stopDiscovery(function() { - if (chrome.runtime.lastError) { - console.error( - 'Error stopping bluetooth discovery: ' + - chrome.runtime.lastError.message); - } - }); - }, - - /** - * Process bluetoothPrivate.onPairing events. - * @param {!chrome.bluetoothPrivate.PairingEvent} e - * @private - */ - onBluetoothPrivateOnPairing_: function(e) { - if (!this.dialogId_ || !this.pairingDevice_ || - e.device.address != this.pairingDevice_.address) { - return; - } - if (e.pairing == chrome.bluetoothPrivate.PairingEventType.KEYS_ENTERED && - e.passkey === undefined && this.pairingEvent_) { - // 'keysEntered' event might not include the updated passkey so preserve - // the current one. - e.passkey = this.pairingEvent_.passkey; - } - this.pairingEvent_ = e; - }, - - /** - * @param {!Event} e - * @private - */ - onAddDeviceTap_: function(e) { - e.preventDefault(); - this.openDialog_('addDevice'); - }, - - /** - * @param {!{detail: {action: string, device: !chrome.bluetooth.Device}}} e - * @private - */ - onDeviceEvent_: function(e) { - var action = e.detail.action; - var device = e.detail.device; - if (action == 'connect') - this.connectDevice_(device); - else if (action == 'disconnect') - this.disconnectDevice_(device); - else if (action == 'remove') - this.forgetDevice_(device); - else - console.error('Unexected action: ' + action); - }, - - /** - * Handle a response sent from the pairing dialog and pass it to the - * bluetoothPrivate API. - * @param {Event} e - * @private - */ - onResponse_: function(e) { - var options = - /** @type {!chrome.bluetoothPrivate.SetPairingResponseOptions} */ ( - e.detail); - this.bluetoothPrivate.setPairingResponse(options, function() { - if (chrome.runtime.lastError) { - // TODO(stevenjb): Show error. - console.error( - 'Error setting pairing response: ' + options.device.name + - ': Response: ' + options.response + - ': Error: ' + chrome.runtime.lastError.message); - } - this.$$('#deviceDialog').close(); - }.bind(this)); - }, - - /** - * @param {string} address - * @return {number} The index of the device associated with |address| or -1. - * @private - */ - getDeviceIndex_: function(address) { - var len = this.deviceList_.length; - for (var i = 0; i < len; ++i) { - if (this.deviceList_[i].address == address) - return i; - } - return -1; - }, - - /** - * @param {!chrome.bluetooth.Device} device - * @return {string} The text to display for |device| in the device list. - * @private - */ - getDeviceName_: function(device) { - return device.name || device.address; - }, - - /** - * @return {!Array<!chrome.bluetooth.Device>} - * @private - */ - getPairedOrConnecting_: function() { - return this.deviceList_.filter(function(device) { - return !!device.paired || !!device.connecting; - }); - }, - - /** - * @return {boolean} True if deviceList contains any paired devices. - * @private - */ - haveDevices_: function() { - return this.deviceList_.findIndex(function(d) { - return !!d.paired; - }) != -1; - }, - - /** - * @param {!chrome.bluetooth.Device} device - * @private - */ - connectDevice_: function(device) { - // If the device is not paired, show the pairing dialog. - if (!device.paired) { - // Set the pairing device and clear any pairing event. - this.pairingDevice_ = device; - this.pairingEvent_ = null; - - this.openDialog_('pairDevice'); - } - - this.bluetoothPrivate.connect(device.address, function(result) { - var error; - if (chrome.runtime.lastError) { - error = chrome.runtime.lastError.message; - } else { - switch (result) { - case chrome.bluetoothPrivate.ConnectResultType.ALREADY_CONNECTED: - case chrome.bluetoothPrivate.ConnectResultType.AUTH_CANCELED: - case chrome.bluetoothPrivate.ConnectResultType.IN_PROGRESS: - case chrome.bluetoothPrivate.ConnectResultType.SUCCESS: - break; - default: - error = result; - } - } - - if (!error) { - this.$$('#deviceDialog').close(); - return; - } - - var name = this.getDeviceName_(device); - var id = 'bluetooth_connect_' + error; - if (this.i18nExists(id)) { - this.errorMessage_ = this.i18n(id, name); - } else { - this.errorMessage_ = error; - console.error('Unexpected error connecting to: ' + name + ': ' + error); - } - this.openDialog_('connectError'); - }.bind(this)); - }, - - /** - * @param {!chrome.bluetooth.Device} device - * @private - */ - disconnectDevice_: function(device) { - this.bluetoothPrivate.disconnectAll(device.address, function() { - if (chrome.runtime.lastError) { - console.error( - 'Error disconnecting: ' + device.address + - chrome.runtime.lastError.message); - } - }); - }, - - /** - * @param {!chrome.bluetooth.Device} device - * @private - */ - forgetDevice_: function(device) { - this.bluetoothPrivate.forgetDevice(device.address, function() { - if (chrome.runtime.lastError) { - console.error( - 'Error forgetting: ' + device.name + ': ' + - chrome.runtime.lastError.message); - } - this.updateDeviceList_(); - }.bind(this)); - }, - - /** - * @param {string} dialogId - * @param {string} dialogToShow The name of the dialog. - * @return {boolean} - * @private - */ - dialogIsVisible_: function(dialogId, dialogToShow) { - return dialogToShow == dialogId; - }, - - /** - * @param {string} dialogId - * @private - */ - openDialog_: function(dialogId) { - if (this.dialogId_) { - // Dialog already opened, just update the contents. - this.dialogId_ = dialogId; - return; - } - this.dialogId_ = dialogId; - // Call flush so that the dialog gets sized correctly before it is opened. - Polymer.dom.flush(); - var dialog = this.$$('#deviceDialog'); - dialog.open(); - this.startDiscovery_(); - }, - - /** @private */ - onDialogClosed_: function() { - this.stopDiscovery_(); - this.dialogId_ = ''; - this.pairingDevice_ = null; - this.pairingEvent_ = null; - }, - - /** - * @param {Event} e - * @private - */ - stopTap_: function(e) { - e.stopPropagation(); - }, + openSubpage_: function() { + settings.navigateTo(settings.Route.BLUETOOTH_DEVICES); + } });
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html b/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html new file mode 100644 index 0000000..3b1368f --- /dev/null +++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html
@@ -0,0 +1,73 @@ +<link rel="import" href="chrome://resources/cr_elements/cr_scrollable_behavior.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/classes/iron-flex-layout.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html"> +<link rel="import" href="/icons.html"> +<link rel="import" href="/settings_shared_css.html"> +<link rel="import" href="bluetooth_device_dialog.html"> +<link rel="import" href="bluetooth_device_list_item.html"> + +<dom-module id="settings-bluetooth-subpage"> + <template> + <style include="settings-shared"> + #container { + @apply(--settings-list-frame-padding); + min-height: 100px; + overflow-y: auto; + } + + #pairButton { + margin: 0 + } + + #topRow { + margin-bottom: 10px; + } + </style> + + <div id="topRow" class="settings-box first"> + <div class="start">[[getOffOnString_(bluetoothEnabled)]]</div> + <paper-button id="pairButton" class="primary-button" + on-tap="onAddDeviceTap_" hidden="[[!bluetoothEnabled]]"> + $i18n{bluetoothPairDevice} + </paper-button> + <paper-toggle-button id="enableBluetooth" checked="{{bluetoothEnabled}}"> + </paper-toggle-button> + </div> + + <div id="container" class="layout vertical" + scrollable on-device-event="onDeviceEvent_" + hidden="[[!showDevices_(bluetoothEnabled, pairedDeviceList_)]]"> + <iron-list id="devices" class="vertical-list" + items="[[pairedDeviceList_]]" + selection-enabled selected-item="{{selectedItem_}}" + scroll-target="container"> + <template> + <bluetooth-device-list-item device="[[item]]" + tabindex$="[[tabIndex]]"> + </bluetooth-device-list-item> + </template> + </iron-list> + </div> + <div id="noDevices" class="settings-box continuation" + hidden$="[[!showNoDevices_(bluetoothEnabled, pairedDeviceList_)]]"> + $i18n{bluetoothNoDevices} + </div> + + <bluetooth-device-dialog id="deviceDialog" + adapter-state="[[adapterState]]" + bluetooth-private="[[bluetoothPrivate]]" + device-list="[[deviceList_]]" + dialog-id="[[dialogId_]]" + error-message="[[errorMessage_]]" + on-close="onDialogClosed_" + on-device-event="onDeviceEvent_" + pairing-device="[[pairingDevice_]]"> + </bluetooth-device-dialog> + + </template> + <script src="bluetooth_subpage.js"></script> +</dom-module>
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.js b/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.js new file mode 100644 index 0000000..50ddb239 --- /dev/null +++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.js
@@ -0,0 +1,415 @@ +// 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. + +/** + * @fileoverview + * 'settings-bluetooth-subpage' is the settings subpage for managing bluetooth + * properties and devices. + */ + +Polymer({ + is: 'settings-bluetooth-subpage', + + behaviors: [I18nBehavior, CrScrollableBehavior], + + properties: { + /** Reflects the bluetooth-page property. */ + bluetoothEnabled: { + type: Boolean, + notify: true, + }, + + /** + * The bluetooth adapter state, cached by bluetooth-page. + * @type {!chrome.bluetooth.AdapterState|undefined} + */ + adapterState: Object, + + /** Informs bluetooth-page whether to show the spinner in the header. */ + showSpinner: { + type: Boolean, + notify: true, + computed: 'computeShowSpinner_(bluetoothEnabled, dialogId_)', + }, + + /** + * The ordered list of bluetooth devices. + * @type {!Array<!chrome.bluetooth.Device>} + * @private + */ + deviceList_: { + type: Array, + value: function() { + return []; + }, + }, + + /** + * The ordered list of paired or connecting bluetooth devices. + * @type {!Array<!chrome.bluetooth.Device>} + */ + pairedDeviceList_: { + type: Array, + value: /** @return {Array} */ function() { + return []; + }, + }, + + /** + * Reflects the iron-list selecteditem property. + * @type {!chrome.bluetooth.Device} + * @private + */ + selectedItem_: { + type: Object, + observer: 'selectedItemChanged_', + }, + + /** + * Set to the name of the dialog to show. This page uses a single + * paper-dialog to host one of three dialog elements, 'addDevice', + * 'pairDevice', or 'connectError'. This allows a seamless transition + * between dialogs. Note: This property should be set before opening the + * dialog and setting the property will not itself cause the dialog to open. + * @private + */ + dialogId_: { + type: String, + value: '', + }, + + /** + * Current Pairing device. + * @type {!chrome.bluetooth.Device|undefined} + * @private + */ + pairingDevice_: Object, + + /** + * The translated error message to show when a connect error occurs. + * @private + */ + errorMessage_: String, + + /** + * Interface for bluetooth calls. Set in bluetooth-page. + * @type {Bluetooth} + * @private + */ + bluetooth: { + type: Object, + value: chrome.bluetooth, + }, + + /** + * Interface for bluetoothPrivate calls. Set in bluetooth-page. + * @type {BluetoothPrivate} + * @private + */ + bluetoothPrivate: { + type: Object, + value: chrome.bluetoothPrivate, + }, + }, + + observers: [ + 'adapterStateChanged_(adapterState.*)', + 'deviceListChanged_(deviceList_.*)', + ], + + /** + * Listener for chrome.bluetooth.onBluetoothDeviceAdded/Changed events. + * @type {function(!chrome.bluetooth.Device)|undefined} + * @private + */ + bluetoothDeviceUpdatedListener_: undefined, + + /** + * Listener for chrome.bluetooth.onBluetoothDeviceRemoved events. + * @type {function(!chrome.bluetooth.Device)|undefined} + * @private + */ + bluetoothDeviceRemovedListener_: undefined, + + /** @override */ + attached: function() { + this.bluetoothDeviceUpdatedListener_ = + this.onBluetoothDeviceUpdated_.bind(this); + this.bluetooth.onDeviceAdded.addListener( + this.bluetoothDeviceUpdatedListener_); + this.bluetooth.onDeviceChanged.addListener( + this.bluetoothDeviceUpdatedListener_); + + this.bluetoothDeviceRemovedListener_ = + this.onBluetoothDeviceRemoved_.bind(this); + this.bluetooth.onDeviceRemoved.addListener( + this.bluetoothDeviceRemovedListener_); + }, + + /** @override */ + detached: function() { + if (this.bluetoothAdapterStateChangedListener_) { + this.bluetooth.onAdapterStateChanged.removeListener( + this.bluetoothAdapterStateChangedListener_); + } + if (this.bluetoothDeviceUpdatedListener_) { + this.bluetooth.onDeviceAdded.removeListener( + this.bluetoothDeviceUpdatedListener_); + this.bluetooth.onDeviceChanged.removeListener( + this.bluetoothDeviceUpdatedListener_); + } + }, + + /** @private */ + computeShowSpinner_: function() { + return this.bluetoothEnabled && !this.dialogId_; + }, + + /** @private */ + adapterStateChanged_: function() { + this.updateDeviceList_(); + }, + + /** @private */ + deviceListChanged_: function() { + var devices = this.$.devices; + this.pairedDeviceList_ = this.deviceList_.filter(function(device) { + return !!device.paired || !!device.connecting; + }); + this.updateScrollableContents(); + }, + + /** @private */ + selectedItemChanged_: function() { + if (this.selectedItem_) + this.connectDevice_(this.selectedItem_); + }, + + /** + * If bluetooth is enabled, request the complete list of devices and update + * this.deviceList_. + * @private + */ + updateDeviceList_: function() { + if (!this.bluetoothEnabled) { + this.deviceList_ = []; + return; + } + this.bluetooth.getDevices(function(devices) { + this.deviceList_ = devices; + }.bind(this)); + }, + + /** + * Process bluetooth.onDeviceAdded and onDeviceChanged events. + * @param {!chrome.bluetooth.Device} device + * @private + */ + onBluetoothDeviceUpdated_: function(device) { + var address = device.address; + if (this.dialogId_ && this.pairingDevice_ && + this.pairingDevice_.address == address) { + this.pairingDevice_ = device; + } + var index = this.deviceList_.findIndex(function(device) { + return device.address == address; + }); + if (index >= 0) { + this.set('deviceList_.' + index, device); + return; + } + this.push('deviceList_', device); + }, + + /** + * Process bluetooth.onDeviceRemoved events. + * @param {!chrome.bluetooth.Device} device + * @private + */ + onBluetoothDeviceRemoved_: function(device) { + var address = device.address; + var index = this.deviceList_.findIndex(function(device) { + return device.address == address; + }); + if (index >= 0) + this.splice('deviceList_', index, 1); + }, + + /** @private */ + startDiscovery_: function() { + if (!this.adapterState || this.adapterState.discovering) + return; + + this.bluetooth.startDiscovery(function() { + var lastError = chrome.runtime.lastError; + if (lastError) { + if (lastError.message == 'Starting discovery failed') + return; // May happen if also started elsewhere, ignore. + console.error('startDiscovery Error: ' + lastError.message); + } + }); + }, + + /** @private */ + stopDiscovery_: function() { + if (!this.get('adapterState.discovering')) + return; + + this.bluetooth.stopDiscovery(function() { + var lastError = chrome.runtime.lastError; + if (lastError) { + if (lastError.message == 'Failed to stop discovery') + return; // May happen if also stopped elsewhere, ignore. + console.error('stopDiscovery Error: ' + lastError.message); + } + }); + }, + + /** + * @param {!Event} e + * @private + */ + onAddDeviceTap_: function(e) { + e.preventDefault(); + this.openDialog_('addDevice'); + }, + + /** + * @param {!{detail: {action: string, device: !chrome.bluetooth.Device}}} e + * @private + */ + onDeviceEvent_: function(e) { + var action = e.detail.action; + var device = e.detail.device; + if (action == 'connect') + this.connectDevice_(device); + else if (action == 'disconnect') + this.disconnectDevice_(device); + else if (action == 'remove') + this.forgetDevice_(device); + else + console.error('Unexected action: ' + action); + }, + + /** + * @return {string} + * @private + */ + getOffOnString_: function() { + return this.i18n(this.bluetoothEnabled ? 'bluetoothOn' : 'bluetoothOff'); + }, + + /** + * @return {boolean} + * @private + */ + showDevices_: function() { + return this.bluetoothEnabled && this.pairedDeviceList_.length > 0; + }, + + /** + * @return {boolean} + * @private + */ + showNoDevices_: function() { + return this.bluetoothEnabled && this.pairedDeviceList_.length == 0; + }, + + /** + * @param {!chrome.bluetooth.Device} device + * @private + */ + connectDevice_: function(device) { + // If the device is not paired, show the pairing dialog before connecting. + if (!device.paired) { + this.pairingDevice_ = device; + this.openDialog_('pairDevice'); + } + + this.bluetoothPrivate.connect(device.address, function(result) { + var error; + if (chrome.runtime.lastError) { + error = chrome.runtime.lastError.message; + } else { + switch (result) { + case chrome.bluetoothPrivate.ConnectResultType.ALREADY_CONNECTED: + case chrome.bluetoothPrivate.ConnectResultType.AUTH_CANCELED: + case chrome.bluetoothPrivate.ConnectResultType.IN_PROGRESS: + case chrome.bluetoothPrivate.ConnectResultType.SUCCESS: + break; + default: + error = result; + } + } + + if (!error) { + this.$.deviceDialog.close(); + return; + } + + var name = device.name || device.address; + var id = 'bluetooth_connect_' + error; + if (this.i18nExists(id)) { + this.errorMessage_ = this.i18n(id, name); + } else { + this.errorMessage_ = error; + console.error('Unexpected error connecting to: ' + name + ': ' + error); + } + this.openDialog_('connectError'); + }.bind(this)); + }, + + /** + * @param {!chrome.bluetooth.Device} device + * @private + */ + disconnectDevice_: function(device) { + this.bluetoothPrivate.disconnectAll(device.address, function() { + if (chrome.runtime.lastError) { + console.error( + 'Error disconnecting: ' + device.address + + chrome.runtime.lastError.message); + } + }); + }, + + /** + * @param {!chrome.bluetooth.Device} device + * @private + */ + forgetDevice_: function(device) { + this.bluetoothPrivate.forgetDevice(device.address, function() { + if (chrome.runtime.lastError) { + console.error( + 'Error forgetting: ' + device.name + ': ' + + chrome.runtime.lastError.message); + } + this.updateDeviceList_(); + }.bind(this)); + }, + + /** + * @param {string} dialogId + * @private + */ + openDialog_: function(dialogId) { + if (this.dialogId_) { + // Dialog already opened, just update the contents. + this.dialogId_ = dialogId; + return; + } + this.dialogId_ = dialogId; + // Call flush so that the dialog gets sized correctly before it is opened. + Polymer.dom.flush(); + this.$.deviceDialog.open(); + this.startDiscovery_(); + }, + + /** @private */ + onDialogClosed_: function() { + this.stopDiscovery_(); + this.dialogId_ = ''; + this.pairingDevice_ = undefined; + }, +});
diff --git a/chrome/browser/resources/settings/bluetooth_page/compiled_resources2.gyp b/chrome/browser/resources/settings/bluetooth_page/compiled_resources2.gyp index 9b6f54f..8930c77 100644 --- a/chrome/browser/resources/settings/bluetooth_page/compiled_resources2.gyp +++ b/chrome/browser/resources/settings/bluetooth_page/compiled_resources2.gyp
@@ -6,6 +6,20 @@ { 'target_name': 'bluetooth_page', 'dependencies': [ + '../compiled_resources2.gyp:route', + '../settings_page/compiled_resources2.gyp:settings_animated_pages', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + '<(EXTERNS_GYP):bluetooth', + '<(EXTERNS_GYP):bluetooth_private', + '<(INTERFACES_GYP):bluetooth_interface', + '<(INTERFACES_GYP):bluetooth_private_interface', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'bluetooth_subpage', + 'dependencies': [ '<(DEPTH)/ui/webui/resources/cr_elements/compiled_resources2.gyp:cr_scrollable_behavior', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', @@ -28,6 +42,7 @@ '<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-input/compiled_resources2.gyp:paper-input-extracted', '<(EXTERNS_GYP):bluetooth', '<(EXTERNS_GYP):bluetooth_private', + '<(INTERFACES_GYP):bluetooth_private_interface', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], },
diff --git a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html index 2e1d3e2e..03336b10 100644 --- a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html +++ b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html
@@ -53,14 +53,14 @@ -webkit-user-select: text; } </style> - <div class="settings-box first"> + <div class="settings-box first three-line"> <settings-toggle-button id="autosigninCheckbox" class="start" pref="{{prefs.credentials_enable_autosignin}}" label="$i18n{passwordsAutosigninLabel}" sub-label="$i18n{passwordsAutosigninDescription}"> </settings-toggle-button> </div> - <div id="manageLink" class="settings-box first"> + <div id="manageLink" class="settings-box first two-line"> <!-- This span lays out the url correctly, relative to the label. --> <span>$i18nRaw{managePasswordsLabel}</span> </div>
diff --git a/chrome/browser/resources/settings/people_page/people_page.html b/chrome/browser/resources/settings/people_page/people_page.html index f84470a..59d922a 100644 --- a/chrome/browser/resources/settings/people_page/people_page.html +++ b/chrome/browser/resources/settings/people_page/people_page.html
@@ -82,6 +82,11 @@ width: 40px; } + /* Avoid orphaned english text (as requested by bettes@) */ + #activity-controls .secondary { + max-width: 97%; + } + #googleg-logo { background-image: url(../../../../../ui/webui/resources/images/200-logo_googleg.png); background-size: cover; @@ -187,7 +192,7 @@ </div> </template> - <div class="settings-box two-line" id="activity-controls" + <div class="settings-box three-line" id="activity-controls" on-tap="onActivityControlsTap_" actionable hidden="[[!syncStatus.signedIn]]"> <div class="icon-container">
diff --git a/chrome/browser/resources/settings/route.js b/chrome/browser/resources/settings/route.js index 388a6b9..c5b132c 100644 --- a/chrome/browser/resources/settings/route.js +++ b/chrome/browser/resources/settings/route.js
@@ -104,6 +104,8 @@ r.INTERNET = r.BASIC.createSection('/internet', 'internet'); r.NETWORK_DETAIL = r.INTERNET.createChild('/networkDetail'); r.KNOWN_NETWORKS = r.INTERNET.createChild('/knownNetworks'); + r.BLUETOOTH = r.BASIC.createSection('/bluetooth', 'bluetooth'); + r.BLUETOOTH_DEVICES = r.BLUETOOTH.createChild('/bluetoothDevices'); // </if> r.APPEARANCE = r.BASIC.createSection('/appearance', 'appearance'); @@ -173,7 +175,6 @@ // <if expr="chromeos"> r.DATETIME = r.ADVANCED.createSection('/dateTime', 'dateTime'); - r.BLUETOOTH = r.ADVANCED.createSection('/bluetooth', 'bluetooth'); // </if> r.PASSWORDS =
diff --git a/chrome/browser/resources/settings/settings_menu/settings_menu.html b/chrome/browser/resources/settings/settings_menu/settings_menu.html index d9458b9..7725ea2 100644 --- a/chrome/browser/resources/settings/settings_menu/settings_menu.html +++ b/chrome/browser/resources/settings/settings_menu/settings_menu.html
@@ -105,6 +105,10 @@ <iron-icon icon="settings:network-wifi"></iron-icon> $i18n{internetPageTitle} </div> + <div data-path="/bluetooth" on-tap="openPage_"> + <iron-icon icon="settings:bluetooth"></iron-icon> + $i18n{bluetoothPageTitle} + </div> </if> <div id="people" data-path="/people" on-tap="openPage_" hidden="[[!pageVisibility.people]]"> @@ -167,12 +171,6 @@ <iron-icon icon="settings:security"></iron-icon> $i18n{privacyPageTitle} </div> -<if expr="chromeos"> - <div data-path="/bluetooth" on-tap="openPage_"> - <iron-icon icon="settings:bluetooth"></iron-icon> - $i18n{bluetoothPageTitle} - </div> -</if> <div data-path="/passwordsAndForms" on-tap="openPage_" hidden="[[!pageVisibility.passwordsAndForms]]"> <iron-icon icon="settings:assignment"></iron-icon>
diff --git a/chrome/browser/resources/settings/settings_page/settings_subpage.html b/chrome/browser/resources/settings/settings_page/settings_subpage.html index 5bb179d..aa43911 100644 --- a/chrome/browser/resources/settings/settings_page/settings_subpage.html +++ b/chrome/browser/resources/settings/settings_page/settings_subpage.html
@@ -2,6 +2,7 @@ <link rel="import" href="chrome://resources/polymer/v1_0/iron-resizable-behavior/iron-resizable-behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable-behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner.html"> <link rel="import" href="/icons.html"> <link rel="import" href="/route.html"> <link rel="import" href="/settings_page/settings_subpage_search.html"> @@ -21,12 +22,20 @@ padding-top: 8px; } + .settings-box.first > h1 { + -webkit-margin-start: 4px; + } + paper-icon-button { /* Centers the ripple on the icon with appropriate margin on right. */ -webkit-margin-end: 8px; -webkit-margin-start: -8px; } + paper-spinner { + @apply(--cr-icon-height-width); + } + h1 { color: var(--settings-nav-grey); flex: 1; /* Push other items to the end. */ @@ -48,6 +57,9 @@ on-search-changed="onSearchChanged_"> </settings-subpage-search> </template> + <template is="dom-if" if="[[showSpinner]]"> + <paper-spinner active></paper-spinner> + </template> <content select=".subpage-title-extra"></content> </div> <content></content>
diff --git a/chrome/browser/resources/settings/settings_page/settings_subpage.js b/chrome/browser/resources/settings/settings_page/settings_subpage.js index a6c4acd..9c1d816 100644 --- a/chrome/browser/resources/settings/settings_page/settings_subpage.js +++ b/chrome/browser/resources/settings/settings_page/settings_subpage.js
@@ -29,6 +29,12 @@ value: '', }, + /** If true shows an active spinner at the end of the subpage header. */ + showSpinner: { + type: Boolean, + value: false, + }, + /** * Indicates which element triggers this subpage. Used by the searching * algorithm to show search bubbles. It is |null| for subpages that are
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd index 82d7443..ef20bb8 100644 --- a/chrome/browser/resources/settings/settings_resources.grd +++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -1009,6 +1009,12 @@ <structure name="IDR_SETTINGS_BLUETOOTH_PAGE_JS" file="bluetooth_page/bluetooth_page.js" type="chrome_html" /> + <structure name="IDR_SETTINGS_BLUETOOTH_SUBPAGE_HTML" + file="bluetooth_page/bluetooth_subpage.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_BLUETOOTH_SUBPAGE_JS" + file="bluetooth_page/bluetooth_subpage.js" + type="chrome_html" /> <structure name="IDR_SETTINGS_DATE_TIME_PAGE_HTML" file="date_time_page/date_time_page.html" type="chrome_html" />
diff --git a/chrome/browser/resources/settings/settings_shared_css.html b/chrome/browser/resources/settings/settings_shared_css.html index 8170604..441b26ab9 100644 --- a/chrome/browser/resources/settings/settings_shared_css.html +++ b/chrome/browser/resources/settings/settings_shared_css.html
@@ -40,6 +40,7 @@ paper-button { height: 32px; margin: 0; + border-radius: 4px; } paper-button[toggles][active] { @@ -253,6 +254,10 @@ min-height: var(--settings-box-two-line-min-height); } + .settings-box.three-line { + min-height: var(--settings-box-three-line-min-height); + } + /* We use an explicit tag to remove the top border, rather than a * :first-of-type modifier. This is a conscious choice, please consult * with dbeam@ or dschuyler@ prior to changing it. */
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.html b/chrome/browser/resources/settings/settings_ui/settings_ui.html index 011015a..be8ebae 100644 --- a/chrome/browser/resources/settings/settings_ui/settings_ui.html +++ b/chrome/browser/resources/settings/settings_ui/settings_ui.html
@@ -33,6 +33,7 @@ color: var(--paper-grey-800); display: flex; flex-direction: column; + line-height: 154%; /* Apply 20px line-height to all texts by default. */ overflow: hidden; /* Prevent double scroll bar bugs. */ }
diff --git a/chrome/browser/resources/settings/settings_vars_css.html b/chrome/browser/resources/settings/settings_vars_css.html index f3665f6e..0cdbeb2a 100644 --- a/chrome/browser/resources/settings/settings_vars_css.html +++ b/chrome/browser/resources/settings/settings_vars_css.html
@@ -40,7 +40,8 @@ /* These are used for the settings-box containers, which may contain one or * more "row items". */ --settings-box-min-height: 48px; - --settings-box-two-line-min-height: 60px; + --settings-box-two-line-min-height: 64px; + --settings-box-three-line-min-height: 84px; --settings-text-elide: { overflow: hidden; @@ -51,7 +52,6 @@ --settings-secondary: { color: var(--paper-grey-600); font-weight: 400; - margin-top: 4px; }; --settings-separator-line: var(--cr-separator-line); @@ -61,7 +61,7 @@ --settings-toggle-bar-size: { height: 12px; left: 4px; - width: 24px; + width: 28px; }; --settings-toggle-button-size: { box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.4); @@ -69,6 +69,12 @@ top: -2px; width: 16px; }; + --settings-toggle-ink-size: { + height: 40px; + top: -12px; + left: -12px; + width: 40px; + }; --settings-toggle-color: var(--google-blue-500); --checkbox-margin-start: 2px; @@ -103,11 +109,18 @@ --paper-toggle-button-checked-bar-color: var(--settings-toggle-color); --paper-toggle-button-checked-button: { @apply(--settings-toggle-button-size); - transform: translate(14px, 0); + transform: translate(18px, 0); }; --paper-toggle-button-checked-button-color: var(--settings-toggle-color); --paper-toggle-button-label-spacing: 0; --paper-toggle-button-unchecked-bar: var(--settings-toggle-bar-size); --paper-toggle-button-unchecked-button: var(--settings-toggle-button-size); + --paper-toggle-button-unchecked-ink: var(--settings-toggle-ink-size); + + --settings-input-underline: { + border-color: var(--paper-grey-300); + } + + --paper-input-container-underline: var(--settings-input-underline); } </style>
diff --git a/chrome/browser/resources/sync_file_system_internals/OWNERS b/chrome/browser/resources/sync_file_system_internals/OWNERS index 70d198f..6bf7d0a 100644 --- a/chrome/browser/resources/sync_file_system_internals/OWNERS +++ b/chrome/browser/resources/sync_file_system_internals/OWNERS
@@ -1,4 +1,7 @@ calvinlo@chromium.org kinuko@chromium.org nhiroki@chromium.org -tzik@chromium.org \ No newline at end of file +tzik@chromium.org + +# TEAM: storage-dev@chromium.org +# COMPONENT: Blink>Storage>FileSystem
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc index af21ad7..c2256a62 100644 --- a/chrome/browser/safe_browsing/safe_browsing_service.cc +++ b/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -20,7 +20,6 @@ #include "base/strings/string_util.h" #include "base/threading/thread.h" #include "base/threading/thread_restrictions.h" -#include "base/threading/worker_pool.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "chrome/browser/browser_process.h" @@ -199,8 +198,7 @@ BrowserThread::GetBlockingPool()->GetSequencedTaskRunner( base::SequencedWorkerPool::GetSequenceToken())); channel_id_service_.reset(new net::ChannelIDService( - new net::DefaultChannelIDStore(channel_id_db.get()), - base::WorkerPool::GetTaskRunner(true))); + new net::DefaultChannelIDStore(channel_id_db.get()))); safe_browsing_request_context_->set_channel_id_service( channel_id_service_.get()); safe_browsing_cookie_store_->SetChannelIDServiceID(
diff --git a/chrome/browser/sync_file_system/OWNERS b/chrome/browser/sync_file_system/OWNERS index 6fd0b8d..db4628f 100644 --- a/chrome/browser/sync_file_system/OWNERS +++ b/chrome/browser/sync_file_system/OWNERS
@@ -1,3 +1,6 @@ kinuko@chromium.org tzik@chromium.org nhiroki@chromium.org + +# TEAM: storage-dev@chromium.org +# COMPONENT: Blink>Storage>FileSystem
diff --git a/chrome/browser/ui/cocoa/framed_browser_window_unittest.mm b/chrome/browser/ui/cocoa/framed_browser_window_unittest.mm index ddb04614..a5d342e 100644 --- a/chrome/browser/ui/cocoa/framed_browser_window_unittest.mm +++ b/chrome/browser/ui/cocoa/framed_browser_window_unittest.mm
@@ -77,8 +77,7 @@ } // Test that undocumented title-hiding API we're using does the job. -// TODO(crbug.com/655112): Fails on Mac 10.11 Tests. -TEST_F(FramedBrowserWindowTest, DISABLED_DoesHideTitle) { +TEST_F(FramedBrowserWindowTest, DoesHideTitle) { // The -display calls are not strictly necessary, but they do // make it easier to see what's happening when debugging (without // them the changes are never flushed to the screen).
diff --git a/chrome/browser/ui/libgtkui/gtk_ui.cc b/chrome/browser/ui/libgtkui/gtk_ui.cc index 4cd4711..b3717b6a 100644 --- a/chrome/browser/ui/libgtkui/gtk_ui.cc +++ b/chrome/browser/ui/libgtkui/gtk_ui.cc
@@ -408,6 +408,7 @@ gtk_widget_realize(fake_window_); // Is this necessary? #elif GTK_MAJOR_VERSION == 3 native_theme_ = NativeThemeGtk3::instance(); + (void)fake_window_; // Silence the unused warning. #else #error "Unsupported GTK version" #endif @@ -863,11 +864,6 @@ UpdateDeviceScaleFactor(); - // Build the various icon tints. - GetNormalButtonTintHSL(&button_tint_); - GetNormalEntryForegroundHSL(&entry_tint_); - GetSelectedEntryForegroundHSL(&selected_entry_tint_); - // We pick the text and background colors for the NTP out of the colors for a // GtkEntry. We do this because GtkEntries background color is never the same // as |toolbar_color|, is usually a white, and when it isn't a white, @@ -973,38 +969,6 @@ #endif } -void GtkUi::GetNormalButtonTintHSL(color_utils::HSL* tint) const { - SkColor accent_color = native_theme_->GetSystemColor( - ui::NativeTheme::kColorId_ProminentButtonColor); - SkColor text_color = native_theme_->GetSystemColor( - ui::NativeTheme::kColorId_LabelEnabledColor); - SkColor base_color = - native_theme_->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground); - - PickButtonTintFromColors(accent_color, text_color, base_color, tint); -} - -void GtkUi::GetNormalEntryForegroundHSL(color_utils::HSL* tint) const { - SkColor accent_color = native_theme_->GetSystemColor( - ui::NativeTheme::kColorId_ProminentButtonColor); - SkColor text_color = native_theme_->GetSystemColor( - ui::NativeTheme::kColorId_TextfieldDefaultColor); - SkColor base_color = native_theme_->GetSystemColor( - ui::NativeTheme::kColorId_TextfieldDefaultBackground); - - PickButtonTintFromColors(accent_color, text_color, base_color, tint); -} - -void GtkUi::GetSelectedEntryForegroundHSL(color_utils::HSL* tint) const { - // The simplest of all the tints. We just use the selected text in the entry - // since the icons tinted this way will only be displayed against - // base[GTK_STATE_SELECTED]. - SkColor color = native_theme_->GetSystemColor( - ui::NativeTheme::kColorId_TextfieldSelectionColor); - - color_utils::SkColorToHSL(color, tint); -} - void GtkUi::UpdateDefaultFont() { GtkWidget* fake_label = gtk_label_new(nullptr); g_object_ref_sink(fake_label); // Remove the floating reference.
diff --git a/chrome/browser/ui/libgtkui/gtk_ui.h b/chrome/browser/ui/libgtkui/gtk_ui.h index ba3dded3..3302327 100644 --- a/chrome/browser/ui/libgtkui/gtk_ui.h +++ b/chrome/browser/ui/libgtkui/gtk_ui.h
@@ -126,23 +126,6 @@ // or generates them per our fallback algorithm. void BuildFrameColors(); - // Gets a tint which depends on the default for |id| as well as |color|. - color_utils::HSL ColorToTint(int id, SkColor color); - - // Returns the tint for buttons that contrasts with the normal window - // background color. - void GetNormalButtonTintHSL(color_utils::HSL* tint) const; - - // Returns a tint that's the color of the current normal text in an entry. - void GetNormalEntryForegroundHSL(color_utils::HSL* tint) const; - - // Returns a tint that's the color of the current highlighted text in an - // entry. - void GetSelectedEntryForegroundHSL(color_utils::HSL* tint) const; - - // Gets a color for the background of the prominent button. - SkColor GetProminentButtonBgColor(int gtk_state) const; - // Updates |default_font_*|. void UpdateDefaultFont(); @@ -159,11 +142,6 @@ // caller while |use_gtk_| is true. ColorMap colors_; - // Colors used to tint certain icons. - color_utils::HSL button_tint_; - color_utils::HSL entry_tint_; - color_utils::HSL selected_entry_tint_; - // Colors that we pass to WebKit. These are generated each time the theme // changes. SkColor focus_ring_color_;
diff --git a/chrome/browser/ui/toolbar/component_toolbar_actions_factory.cc b/chrome/browser/ui/toolbar/component_toolbar_actions_factory.cc index 487a59a..334bda9 100644 --- a/chrome/browser/ui/toolbar/component_toolbar_actions_factory.cc +++ b/chrome/browser/ui/toolbar/component_toolbar_actions_factory.cc
@@ -10,6 +10,7 @@ #include "chrome/browser/media/router/media_router_feature.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/toolbar/media_router_action_controller.h" #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h" #include "extensions/common/feature_switch.h" @@ -44,6 +45,11 @@ std::set<std::string> ComponentToolbarActionsFactory::GetInitialComponentIds( Profile* profile) { std::set<std::string> component_ids; + if (media_router::MediaRouterEnabled(profile) && + MediaRouterActionController::IsActionShownByPolicy(profile)) { + component_ids.insert(kMediaRouterActionId); + } + return component_ids; }
diff --git a/chrome/browser/ui/toolbar/media_router_action_controller.cc b/chrome/browser/ui/toolbar/media_router_action_controller.cc index 9db79c8..feaca76 100644 --- a/chrome/browser/ui/toolbar/media_router_action_controller.cc +++ b/chrome/browser/ui/toolbar/media_router_action_controller.cc
@@ -24,6 +24,17 @@ DCHECK_EQ(dialog_count_, 0u); } +// static +bool MediaRouterActionController::IsActionShownByPolicy(Profile* profile) { + CHECK(profile); + const PrefService::Preference* pref = + profile->GetPrefs()->FindPreference(prefs::kShowCastIconInToolbar); + bool show = false; + if (pref->IsManaged()) + pref->GetValue()->GetAsBoolean(&show); + return show; +} + void MediaRouterActionController::OnIssue(const media_router::Issue& issue) { has_issue_ = true; MaybeAddOrRemoveAction(); @@ -68,8 +79,10 @@ media_router::MediaRoutesObserver(router), profile_(profile), component_action_delegate_(component_action_delegate), - component_migration_helper_(component_migration_helper) { - DCHECK(profile_); + component_migration_helper_(component_migration_helper), + shown_by_policy_( + MediaRouterActionController::IsActionShownByPolicy(profile)) { + CHECK(profile_); media_router::IssuesObserver::Init(); pref_change_registrar_.Init(profile->GetPrefs()); pref_change_registrar_.Add( @@ -93,7 +106,8 @@ } bool MediaRouterActionController::ShouldEnableAction() const { - return has_local_display_route_ || has_issue_ || dialog_count_ || + return shown_by_policy_ || has_local_display_route_ || has_issue_ || + dialog_count_ || component_migration_helper_->GetComponentActionPref( ComponentToolbarActionsFactory::kMediaRouterActionId); }
diff --git a/chrome/browser/ui/toolbar/media_router_action_controller.h b/chrome/browser/ui/toolbar/media_router_action_controller.h index 346d8c5..9a19f77 100644 --- a/chrome/browser/ui/toolbar/media_router_action_controller.h +++ b/chrome/browser/ui/toolbar/media_router_action_controller.h
@@ -31,6 +31,9 @@ ComponentMigrationHelper* component_migration_helper); ~MediaRouterActionController() override; + // Whether the media router action is shown by an administrator policy. + static bool IsActionShownByPolicy(Profile* profile); + // media_router::IssuesObserver: void OnIssue(const media_router::Issue& issue) override; void OnIssuesCleared() override; @@ -77,6 +80,9 @@ bool has_issue_ = false; bool has_local_display_route_ = false; + // Whether the media router action is shown by an administrator policy. + bool shown_by_policy_; + // The number of dialogs that are currently open. size_t dialog_count_ = 0;
diff --git a/chrome/browser/ui/toolbar/media_router_contextual_menu.cc b/chrome/browser/ui/toolbar/media_router_contextual_menu.cc index a7d5bc4e..a24fdb8c 100644 --- a/chrome/browser/ui/toolbar/media_router_contextual_menu.cc +++ b/chrome/browser/ui/toolbar/media_router_contextual_menu.cc
@@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "chrome/browser/ui/toolbar/media_router_contextual_menu.h" + #include <string> #include "base/logging.h" @@ -15,7 +17,7 @@ #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/singleton_tabs.h" #include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h" -#include "chrome/browser/ui/toolbar/media_router_contextual_menu.h" +#include "chrome/browser/ui/toolbar/media_router_action_controller.h" #include "chrome/browser/ui/toolbar/toolbar_actions_model.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" @@ -24,9 +26,17 @@ #include "components/signin/core/browser/signin_manager.h" #include "extensions/common/constants.h" #include "ui/base/models/menu_model_delegate.h" - +#include "ui/gfx/color_palette.h" +#include "ui/gfx/paint_vector_icon.h" MediaRouterContextualMenu::MediaRouterContextualMenu(Browser* browser) + : MediaRouterContextualMenu( + browser, + MediaRouterActionController::IsActionShownByPolicy( + browser->profile())) {} + +MediaRouterContextualMenu::MediaRouterContextualMenu(Browser* browser, + bool shown_by_policy) : browser_(browser), menu_model_(this), component_migration_helper_(ToolbarActionsModel::Get(browser->profile()) @@ -40,9 +50,18 @@ IDS_MEDIA_ROUTER_LEARN_MORE); menu_model_.AddItemWithStringId(IDC_MEDIA_ROUTER_HELP, IDS_MEDIA_ROUTER_HELP); - menu_model_.AddCheckItemWithStringId( - IDC_MEDIA_ROUTER_ALWAYS_SHOW_TOOLBAR_ACTION, - IDS_MEDIA_ROUTER_ALWAYS_SHOW_TOOLBAR_ACTION); + if (shown_by_policy) { + menu_model_.AddItemWithStringId(IDC_MEDIA_ROUTER_SHOWN_BY_POLICY, + IDS_MEDIA_ROUTER_SHOWN_BY_POLICY); + menu_model_.SetIcon( + menu_model_.GetIndexOfCommandId(IDC_MEDIA_ROUTER_SHOWN_BY_POLICY), + gfx::Image(gfx::CreateVectorIcon(gfx::VectorIconId::BUSINESS, 16, + gfx::kChromeIconGrey))); + } else { + menu_model_.AddCheckItemWithStringId( + IDC_MEDIA_ROUTER_ALWAYS_SHOW_TOOLBAR_ACTION, + IDS_MEDIA_ROUTER_ALWAYS_SHOW_TOOLBAR_ACTION); + } menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS) menu_model_.AddItemWithStringId(IDC_MEDIA_ROUTER_MANAGE_DEVICES, @@ -71,14 +90,13 @@ return browser_->profile()->GetPrefs()->GetBoolean( prefs::kMediaRouterEnableCloudServices); } - if (command_id == IDC_MEDIA_ROUTER_ALWAYS_SHOW_TOOLBAR_ACTION) { + if (command_id == IDC_MEDIA_ROUTER_ALWAYS_SHOW_TOOLBAR_ACTION) return GetAlwaysShowActionPref(); - } return false; } bool MediaRouterContextualMenu::IsCommandIdEnabled(int command_id) const { - return true; + return command_id != IDC_MEDIA_ROUTER_SHOWN_BY_POLICY; } bool MediaRouterContextualMenu::IsCommandIdVisible(int command_id) const {
diff --git a/chrome/browser/ui/toolbar/media_router_contextual_menu.h b/chrome/browser/ui/toolbar/media_router_contextual_menu.h index 32bd1ca..490814bd 100644 --- a/chrome/browser/ui/toolbar/media_router_contextual_menu.h +++ b/chrome/browser/ui/toolbar/media_router_contextual_menu.h
@@ -19,6 +19,9 @@ class MediaRouterContextualMenu : public ui::SimpleMenuModel::Delegate { public: explicit MediaRouterContextualMenu(Browser* browser); + + // Constructor for injecting values in tests. + MediaRouterContextualMenu(Browser* browser, bool shown_by_policy); ~MediaRouterContextualMenu() override; ui::MenuModel* menu_model() { return &menu_model_; } @@ -28,6 +31,8 @@ ToggleCloudServicesItem); FRIEND_TEST_ALL_PREFIXES(MediaRouterContextualMenuUnitTest, ToggleAlwaysShowIconItem); + FRIEND_TEST_ALL_PREFIXES(MediaRouterContextualMenuUnitTest, + ActionShownByPolicy); // Gets or sets the "Always show icon" option. bool GetAlwaysShowActionPref() const;
diff --git a/chrome/browser/ui/toolbar/media_router_contextual_menu_unittest.cc b/chrome/browser/ui/toolbar/media_router_contextual_menu_unittest.cc index 2b2e9b64..17730a7 100644 --- a/chrome/browser/ui/toolbar/media_router_contextual_menu_unittest.cc +++ b/chrome/browser/ui/toolbar/media_router_contextual_menu_unittest.cc
@@ -15,6 +15,18 @@ #include "chrome/browser/ui/toolbar/media_router_contextual_menu.h" #include "chrome/test/base/browser_with_test_window_test.h" +namespace { + +bool HasCommandId(ui::MenuModel* menu_model, int command_id) { + for (int i = 0; i < menu_model->GetItemCount(); i++) { + if (menu_model->GetCommandIdAt(i) == command_id) + return true; + } + return false; +} + +} // namespace + class MediaRouterContextualMenuUnitTest : public BrowserWithTestWindowTest { public: MediaRouterContextualMenuUnitTest() {} @@ -150,3 +162,16 @@ EXPECT_FALSE(component_migration_helper->GetComponentActionPref( ComponentToolbarActionsFactory::kMediaRouterActionId)); } + +TEST_F(MediaRouterContextualMenuUnitTest, ActionShownByPolicy) { + // Create a contextual menu for an icon shown by administrator policy. + MediaRouterContextualMenu menu(browser(), true); + + // The item "Added by your administrator" should be shown disabled. + EXPECT_TRUE(menu.IsCommandIdVisible(IDC_MEDIA_ROUTER_SHOWN_BY_POLICY)); + EXPECT_FALSE(menu.IsCommandIdEnabled(IDC_MEDIA_ROUTER_SHOWN_BY_POLICY)); + + // The checkbox item "Always show icon" should not be shown. + EXPECT_FALSE(HasCommandId(menu.menu_model(), + IDC_MEDIA_ROUTER_ALWAYS_SHOW_TOOLBAR_ACTION)); +}
diff --git a/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc index 2916896e..72124314 100644 --- a/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc +++ b/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc
@@ -33,6 +33,20 @@ #include "ui/views/mus/mus_client.h" #include "ui/views/mus/mus_property_mirror.h" +namespace { + +void MirrorIcon(aura::Window* window, + aura::Window* root_window, + const aura::WindowProperty<gfx::ImageSkia*>* key) { + gfx::ImageSkia* value = window->GetProperty(key); + if (!value || value->isNull()) + root_window->ClearProperty(key); + else + root_window->SetProperty(key, new gfx::ImageSkia(*value)); +} + +} // namespace + // Relays aura content window properties to its root window (the mash frame). // Ash relies on various window properties for frame titles, shelf items, etc. // These properties are read from the client's root, not child content windows. @@ -52,8 +66,7 @@ int32_t value = window->GetProperty(ash::kShelfItemTypeKey); root_window->SetProperty(ash::kShelfItemTypeKey, value); } else if (key == aura::client::kAppIconKey) { - gfx::ImageSkia* value = window->GetProperty(aura::client::kAppIconKey); - root_window->SetProperty(aura::client::kAppIconKey, value); + MirrorIcon(window, root_window, aura::client::kAppIconKey); } else if (key == aura::client::kAppIdKey) { std::string* value = window->GetProperty(aura::client::kAppIdKey); root_window->SetProperty(aura::client::kAppIdKey, value); @@ -64,8 +77,7 @@ base::string16* value = window->GetProperty(aura::client::kTitleKey); root_window->SetProperty(aura::client::kTitleKey, value); } else if (key == aura::client::kWindowIconKey) { - gfx::ImageSkia* value = window->GetProperty(aura::client::kWindowIconKey); - root_window->SetProperty(aura::client::kWindowIconKey, value); + MirrorIcon(window, root_window, aura::client::kWindowIconKey); } }
diff --git a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc index eca7168a..68ea2e4 100644 --- a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc +++ b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
@@ -30,14 +30,8 @@ namespace { ui::NativeTheme* GetNativeThemeForWindow(aura::Window* window) { - if (!window) - return nullptr; - Profile* profile = nullptr; - // Window types not listed here (such as tooltips) will never use Chrome - // theming. - if (window->type() == ui::wm::WINDOW_TYPE_NORMAL || - window->type() == ui::wm::WINDOW_TYPE_POPUP) { + if (window) { profile = reinterpret_cast<Profile*>( window->GetNativeWindowProperty(Profile::kProfileKey)); }
diff --git a/chrome/browser/ui/views/chrome_views_delegate.cc b/chrome/browser/ui/views/chrome_views_delegate.cc index 96f14b1..88741a1 100644 --- a/chrome/browser/ui/views/chrome_views_delegate.cc +++ b/chrome/browser/ui/views/chrome_views_delegate.cc
@@ -450,20 +450,23 @@ // While the majority of the time, context wasn't plumbed through due to the // existence of a global WindowParentingClient, if this window is toplevel, // it's possible that there is no contextual state that we can use. - if (params->parent == NULL && params->context == NULL && !params->child) { - params->native_widget = new views::DesktopNativeWidgetAura(delegate); - } else if (use_non_toplevel_window) { + gfx::NativeWindow parent_or_context = + params->parent ? params->parent : params->context; + void* profile = + parent_or_context + ? parent_or_context->GetNativeWindowProperty(Profile::kProfileKey) + : nullptr; + if ((!params->parent && !params->context && !params->child) || + !use_non_toplevel_window) { + views::DesktopNativeWidgetAura* native_widget = + new views::DesktopNativeWidgetAura(delegate); + params->native_widget = native_widget; + native_widget->SetNativeWindowProperty(Profile::kProfileKey, profile); + } else { views::NativeWidgetAura* native_widget = new views::NativeWidgetAura(delegate); - if (params->parent) { - Profile* parent_profile = reinterpret_cast<Profile*>( - params->parent->GetNativeWindowProperty(Profile::kProfileKey)); - native_widget->SetNativeWindowProperty(Profile::kProfileKey, - parent_profile); - } params->native_widget = native_widget; - } else { - params->native_widget = new views::DesktopNativeWidgetAura(delegate); + native_widget->SetNativeWindowProperty(Profile::kProfileKey, profile); } #endif }
diff --git a/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc b/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc index f102bd0..91bba25 100644 --- a/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc +++ b/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc
@@ -517,6 +517,12 @@ void DragImageBetweenFrames_Step2(DragImageBetweenFrames_TestState*); void DragImageBetweenFrames_Step3(DragImageBetweenFrames_TestState*); + struct DragImageFromDisappearingFrame_TestState; + void DragImageFromDisappearingFrame_Step2( + DragImageFromDisappearingFrame_TestState*); + void DragImageFromDisappearingFrame_Step3( + DragImageFromDisappearingFrame_TestState*); + struct CrossSiteDrag_TestState; void CrossSiteDrag_Step2(CrossSiteDrag_TestState*); void CrossSiteDrag_Step3(CrossSiteDrag_TestState*); @@ -1052,6 +1058,122 @@ // There is no known way to execute test-controlled tasks during // a drag-and-drop loop run by Windows OS. #if defined(OS_WIN) +#define MAYBE_DragImageFromDisappearingFrame \ + DISABLED_DragImageFromDisappearingFrame +#else +#define MAYBE_DragImageFromDisappearingFrame DragImageFromDisappearingFrame +#endif + +// Data that needs to be shared across multiple test steps below +// (i.e. across DragImageFromDisappearingFrame_Step2 and +// DragImageFromDisappearingFrame_Step3). +struct DragAndDropBrowserTest::DragImageFromDisappearingFrame_TestState { + DOMDragEventVerifier expected_dom_event_data; + std::unique_ptr<DOMDragEventWaiter> drop_event_waiter; +}; + +// Scenario: drag an image from the left into the right frame and delete the +// left frame during the drag. This is a regression test for +// https://crbug.com/670123. +// Test coverage: dragenter, dragover, drop DOM events. +IN_PROC_BROWSER_TEST_P(DragAndDropBrowserTest, + MAYBE_DragImageFromDisappearingFrame) { + // Load the test page. + std::string frame_site = use_cross_site_subframe() ? "b.com" : "a.com"; + ASSERT_TRUE(NavigateToTestPage("a.com")); + ASSERT_TRUE(NavigateLeftFrame(frame_site, "image_source.html")); + ASSERT_TRUE(NavigateRightFrame(frame_site, "drop_target.html")); + + // Setup test expectations. + DragAndDropBrowserTest::DragImageFromDisappearingFrame_TestState state; + state.expected_dom_event_data.set_expected_drop_effect("none"); + // (dragstart event handler in image_source.html is asking for "copy" only). + state.expected_dom_event_data.set_expected_effect_allowed("copy"); + state.expected_dom_event_data.set_expected_mime_types( + "text/html,text/plain,text/uri-list"); + state.expected_dom_event_data.set_expected_client_position("(155, 150)"); + state.expected_dom_event_data.set_expected_page_position("(155, 150)"); + + // Start the drag in the left frame. + DragStartWaiter drag_start_waiter(web_contents()); + drag_start_waiter.PostTaskWhenDragStarts( + base::Bind(&DragAndDropBrowserTest::DragImageFromDisappearingFrame_Step2, + base::Unretained(this), base::Unretained(&state))); + EXPECT_TRUE(SimulateMouseDownAndDragStartInLeftFrame()); + + // The next step of the test (DragImageFromDisappearingFrame_Step2) runs + // inside the nested drag-and-drop message loop - the call below won't return + // until the drag-and-drop has already ended. + drag_start_waiter.WaitUntilDragStart(nullptr, nullptr, nullptr, nullptr); + + DragImageFromDisappearingFrame_Step3(&state); +} + +void DragAndDropBrowserTest::DragImageFromDisappearingFrame_Step2( + DragAndDropBrowserTest::DragImageFromDisappearingFrame_TestState* state) { + // Delete the left frame in an attempt to repro https://crbug.com/670123. + content::RenderFrameDeletedObserver frame_deleted_observer(left_frame()); + ASSERT_TRUE(ExecuteScript(web_contents()->GetMainFrame(), + "frame = document.getElementById('left');\n" + "frame.parentNode.removeChild(frame);\n")); + frame_deleted_observer.WaitUntilDeleted(); + + // While dragging, move mouse from the left into the right frame. + // This should trigger dragleave and dragenter events. + { + DOMDragEventWaiter dragenter_event_waiter("dragenter", right_frame()); + ASSERT_TRUE(SimulateMouseMoveToRightFrame()); + + { // Verify dragenter DOM event. + std::string dragenter_event; + EXPECT_TRUE( + dragenter_event_waiter.WaitForNextMatchingEvent(&dragenter_event)); + EXPECT_THAT(dragenter_event, state->expected_dom_event_data.Matches()); + } + + // Note that ash (unlike aura/x11) will not fire dragover event in response + // to the same mouse event that trigerred a dragenter. Because of that, we + // postpone dragover testing until the next test step below. See + // implementation of ash::DragDropController::DragUpdate for details. + } + + // Move the mouse twice in the right frame. The 1st move will ensure that + // allowed operations communicated by the renderer will be stored in + // WebContentsViewAura::current_drag_op_. The 2nd move will ensure that this + // gets be copied into DesktopDragDropClientAuraX11::negotiated_operation_. + for (int i = 0; i < 2; i++) { + DOMDragEventWaiter dragover_event_waiter("dragover", right_frame()); + ASSERT_TRUE(SimulateMouseMoveToRightFrame()); + + { // Verify dragover DOM event. + std::string dragover_event; + EXPECT_TRUE( + dragover_event_waiter.WaitForNextMatchingEvent(&dragover_event)); + EXPECT_THAT(dragover_event, state->expected_dom_event_data.Matches()); + } + } + + // Release the mouse button to end the drag. + state->drop_event_waiter.reset(new DOMDragEventWaiter("drop", right_frame())); + SimulateMouseUp(); + // The test will continue in DragImageFromDisappearingFrame_Step3. +} + +void DragAndDropBrowserTest::DragImageFromDisappearingFrame_Step3( + DragAndDropBrowserTest::DragImageFromDisappearingFrame_TestState* state) { + // Verify drop DOM event. + { + std::string drop_event; + EXPECT_TRUE( + state->drop_event_waiter->WaitForNextMatchingEvent(&drop_event)); + state->drop_event_waiter.reset(); + EXPECT_THAT(drop_event, state->expected_dom_event_data.Matches()); + } +} + +// There is no known way to execute test-controlled tasks during +// a drag-and-drop loop run by Windows OS. +#if defined(OS_WIN) #define MAYBE_CrossSiteDrag DISABLED_CrossSiteDrag #else #define MAYBE_CrossSiteDrag CrossSiteDrag
diff --git a/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc index a091653..34363d31 100644 --- a/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc
@@ -6,11 +6,14 @@ #include "base/i18n/timezone.h" #include "chrome/browser/chromeos/arc/optin/arc_optin_preference_handler.h" +#include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor_observer.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" +#include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" #include "components/login/localized_values_builder.h" +#include "components/prefs/pref_service.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui.h" #include "ui/base/l10n/l10n_util.h" @@ -29,9 +32,8 @@ ArcTermsOfServiceScreenHandler::~ArcTermsOfServiceScreenHandler() { system::TimezoneSettings::GetInstance()->RemoveObserver(this); - - if (screen_) - screen_->OnActorDestroyed(this); + for (auto& observer : observer_list_) + observer.OnActorDestroyed(this); } void ArcTermsOfServiceScreenHandler::RegisterMessages() { @@ -113,8 +115,14 @@ CallJS("setLocationServicesMode", enabled, managed); } -void ArcTermsOfServiceScreenHandler::SetDelegate(Delegate* screen) { - screen_ = screen; +void ArcTermsOfServiceScreenHandler::AddObserver( + ArcTermsOfServiceScreenActorObserver* observer) { + observer_list_.AddObserver(observer); +} + +void ArcTermsOfServiceScreenHandler::RemoveObserver( + ArcTermsOfServiceScreenActorObserver* observer) { + observer_list_.RemoveObserver(observer); } void ArcTermsOfServiceScreenHandler::Show() { @@ -142,6 +150,12 @@ void ArcTermsOfServiceScreenHandler::DoShow() { Profile* profile = ProfileManager::GetActiveUserProfile(); CHECK(profile); + + // Enable ARC to match ArcSessionManager logic. ArcSessionManager expects that + // ARC is enabled (prefs::kArcEnabled = true) on showing Terms of Service. If + // user accepts ToS then prefs::kArcEnabled is left activated. If user skips + // ToS then prefs::kArcEnabled is automatically reset in ArcSessionManager. + profile->GetPrefs()->SetBoolean(prefs::kArcEnabled, true); pref_handler_.reset(new arc::ArcOptInPreferenceHandler( this, profile->GetPrefs())); pref_handler_->Start(); @@ -153,18 +167,17 @@ } void ArcTermsOfServiceScreenHandler::HandleSkip() { - if (screen_) - screen_->OnSkip(); + for (auto& observer : observer_list_) + observer.OnSkip(); } void ArcTermsOfServiceScreenHandler::HandleAccept( bool enable_backup_restore, bool enable_location_services) { - if (screen_) { - pref_handler_->EnableBackupRestore(enable_backup_restore); - pref_handler_->EnableLocationService(enable_location_services); - screen_->OnAccept(); - } + pref_handler_->EnableBackupRestore(enable_backup_restore); + pref_handler_->EnableLocationService(enable_location_services); + for (auto& observer : observer_list_) + observer.OnAccept(); } } // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h index 0b17170..bd0a1f33 100644 --- a/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h +++ b/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h
@@ -9,6 +9,7 @@ #include <string> #include "base/macros.h" +#include "base/observer_list.h" #include "chrome/browser/chromeos/arc/optin/arc_optin_preference_handler_observer.h" #include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_actor.h" #include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h" @@ -38,7 +39,8 @@ ::login::LocalizedValuesBuilder* builder) override; // ArcTermsOfServiceScreenActor: - void SetDelegate(Delegate* screen) override; + void AddObserver(ArcTermsOfServiceScreenActorObserver* observer) override; + void RemoveObserver(ArcTermsOfServiceScreenActorObserver* observer) override; void Show() override; void Hide() override; @@ -60,7 +62,8 @@ void OnBackupAndRestoreModeChanged(bool enabled, bool managed) override; void OnLocationServicesModeChanged(bool enabled, bool managed) override; - Delegate* screen_ = nullptr; + base::ObserverList<ArcTermsOfServiceScreenActorObserver, true> + observer_list_; // Whether the screen should be shown right after initialization. bool show_on_init_ = false;
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc index c6bd61e9..80fd859 100644 --- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc +++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -331,6 +331,8 @@ IDS_SETTINGS_BLUETOOTH_EXPAND_ACCESSIBILITY_LABEL}, {"bluetoothNoDevices", IDS_SETTINGS_BLUETOOTH_NO_DEVICES}, {"bluetoothNotConnected", IDS_SETTINGS_BLUETOOTH_NOT_CONNECTED}, + {"bluetoothOff", IDS_SETTINGS_BLUETOOTH_OFF}, + {"bluetoothOn", IDS_SETTINGS_BLUETOOTH_ON}, {"bluetoothPageTitle", IDS_SETTINGS_BLUETOOTH}, {"bluetoothPair", IDS_SETTINGS_BLUETOOTH_PAIR}, {"bluetoothPairDevice", IDS_SETTINGS_BLUETOOTH_PAIR_DEVICE},
diff --git a/chrome/browser/ui/webui/sync_file_system_internals/OWNERS b/chrome/browser/ui/webui/sync_file_system_internals/OWNERS index 70d198f..c00084d 100644 --- a/chrome/browser/ui/webui/sync_file_system_internals/OWNERS +++ b/chrome/browser/ui/webui/sync_file_system_internals/OWNERS
@@ -1,4 +1,4 @@ calvinlo@chromium.org kinuko@chromium.org nhiroki@chromium.org -tzik@chromium.org \ No newline at end of file +tzik@chromium.org
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index c168f848..15a6efb 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -191,7 +191,7 @@ // The lock screen will be preloaded so it is instantly available when the user // locks the Chromebook device. const base::Feature kPreloadLockScreen{"PreloadLockScreen", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; #endif // Enables the Print Scaling feature in print preview.
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index 01658e34..cdb94ce 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc
@@ -1971,6 +1971,11 @@ #if defined(ENABLE_MEDIA_ROUTER) // Pref name for the policy controlling whether to enable Media Router. const char kEnableMediaRouter[] = "media_router.enable_media_router"; +#if !defined(OS_ANDROID) +// Pref name for the policy controlling whether to force the Cast icon to be +// shown in the toolbar/overflow menu. +const char kShowCastIconInToolbar[] = "media_router.show_cast_icon_in_toolbar"; +#endif // !defined(OS_ANDROID) #endif // defined(ENABLE_MEDIA_ROUTER) // *************** SERVICE PREFS ***************
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 5bdcc20..3a2daef 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h
@@ -737,6 +737,9 @@ #if defined(ENABLE_MEDIA_ROUTER) extern const char kEnableMediaRouter[]; +#if !defined(OS_ANDROID) +extern const char kShowCastIconInToolbar[]; +#endif // !defined(OS_ANDROID) #endif // defined(ENABLE_MEDIA_ROUTER) #if !defined(OS_ANDROID)
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc index 98ce791..b15681a 100644 --- a/chrome/renderer/chrome_content_renderer_client.cc +++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -987,14 +987,14 @@ // and component extensions. Also allow dev interfaces when --enable-nacl // is set, but do not allow --enable-nacl to provide dev interfaces to // webstore installed and other normally allowed URLs. - WebString dev_attribute = WebString::fromUTF8("@dev"); + std::string dev_attribute("@dev"); if (is_extension_unrestricted || (is_nacl_unrestricted && !is_nacl_allowed_by_location)) { // Add the special '@dev' attribute. std::vector<base::string16> param_names; std::vector<base::string16> param_values; - param_names.push_back(dev_attribute); - param_values.push_back(WebString()); + param_names.push_back(base::ASCIIToUTF16(dev_attribute)); + param_values.push_back(base::string16()); AppendParams( param_names, param_values, @@ -1004,8 +1004,10 @@ // If the params somehow contain '@dev', remove it. size_t attribute_count = params->attributeNames.size(); for (size_t i = 0; i < attribute_count; ++i) { - if (params->attributeNames[i].equals(dev_attribute)) + if (params->attributeNames[i].equals(dev_attribute.data(), + dev_attribute.length())) { params->attributeNames[i] = WebString(); + } } } }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index df42aa6..3ed44116 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -1032,7 +1032,6 @@ "data/webui/settings/advanced_page_browsertest.js", "data/webui/settings/animation_browsertest.js", "data/webui/settings/basic_page_browsertest.js", - "data/webui/settings/bluetooth_page_browsertest_chromeos.js", "data/webui/settings/cr_settings_browsertest.js", "data/webui/settings/easy_unlock_browsertest_chromeos.js", "data/webui/settings/help_page_browsertest.js", @@ -1056,7 +1055,6 @@ if (!is_chromeos) { sources -= [ "data/webui/certificate_viewer_dialog_test.js", - "data/webui/settings/bluetooth_page_browsertest_chromeos.js", "data/webui/settings/easy_unlock_browsertest_chromeos.js", ] } else { @@ -2376,6 +2374,7 @@ "../browser/ui/webui/signin/user_manager_ui_browsertest.cc", ] deps += [ + "//chrome/browser/chromeos:arc_test_support", "//chromeos/ime:gencode", "//components/arc:arc_test_support", "//components/user_manager:test_support",
diff --git a/chrome/test/data/extensions/api_test/native_bindings/background.js b/chrome/test/data/extensions/api_test/native_bindings/background.js index bf18b8d..58b4edb2 100644 --- a/chrome/test/data/extensions/api_test/native_bindings/background.js +++ b/chrome/test/data/extensions/api_test/native_bindings/background.js
@@ -86,6 +86,14 @@ tabId = tab.id; }); }, + function castStreaming() { + // chrome.cast.streaming APIs are the only ones that are triply-prefixed. + chrome.test.assertTrue(!!chrome.cast); + chrome.test.assertTrue(!!chrome.cast.streaming); + chrome.test.assertTrue(!!chrome.cast.streaming.udpTransport); + chrome.test.assertTrue(!!chrome.cast.streaming.udpTransport.setOptions); + chrome.test.succeed(); + } ]; chrome.test.getConfig(config => {
diff --git a/chrome/test/data/extensions/api_test/native_bindings/manifest.json b/chrome/test/data/extensions/api_test/native_bindings/manifest.json index 5e2d2c0b..d189c9ad 100644 --- a/chrome/test/data/extensions/api_test/native_bindings/manifest.json +++ b/chrome/test/data/extensions/api_test/native_bindings/manifest.json
@@ -1,9 +1,10 @@ { "name": "Simple e2e", - "description": "A simple e2e test for native bindings", + "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8xv6iO+j4kzj1HiBL93+XVJH/CRyAQMUHS/Z0l8nCAzaAFkW/JsNwxJqQhrZspnxLqbQxNncXs6g6bsXAwKHiEs+LSs+bIv0Gc/2ycZdhXJ8GhEsSMakog5dpQd1681c2gLK/8CrAoewE/0GIKhaFcp7a2iZlGh4Am6fgMKy0iQIDAQAB", + "description": "A simple e2e test for native bindings, id is ddchlicdkolnonkihahngkmmmjnjlkkf", "manifest_version": 2, "version": "0.1", - "permissions": ["idle", "tabs"], + "permissions": ["idle", "tabs", "cast.streaming"], "background": { "persistent": false, "page": "background.html"
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json index 3e9a758..782d5e8ab 100644 --- a/chrome/test/data/policy/policy_test_cases.json +++ b/chrome/test/data/policy/policy_test_cases.json
@@ -2035,6 +2035,14 @@ ] }, + "ShowCastIconInToolbar": { + "os": ["win", "linux", "mac", "chromeos"], + "test_policy": { "ShowCastIconInToolbar": false }, + "pref_mappings": [ + { "pref": "media_router.show_cast_icon_in_toolbar" } + ] + }, + "NTPContentSuggestionsEnabled": { "os": ["android"], "test_policy": { "NTPContentSuggestionsEnabled": false },
diff --git a/chrome/test/data/webui/settings/advanced_page_browsertest.js b/chrome/test/data/webui/settings/advanced_page_browsertest.js index e8652d37..fd31d27 100644 --- a/chrome/test/data/webui/settings/advanced_page_browsertest.js +++ b/chrome/test/data/webui/settings/advanced_page_browsertest.js
@@ -45,7 +45,7 @@ var sections = ['privacy', 'passwordsAndForms', 'languages', 'downloads', 'reset']; if (cr.isChromeOS) - sections = sections.concat(['dateTime', 'bluetooth', 'a11y']); + sections = sections.concat(['dateTime', 'a11y']); for (var i = 0; i < sections.length; i++) { var section = self.getSection(page, sections[i], true /* advanced */);
diff --git a/chrome/test/data/webui/settings/basic_page_browsertest.js b/chrome/test/data/webui/settings/basic_page_browsertest.js index de32ad4..9a290f7 100644 --- a/chrome/test/data/webui/settings/basic_page_browsertest.js +++ b/chrome/test/data/webui/settings/basic_page_browsertest.js
@@ -91,7 +91,7 @@ if (!cr.isChromeOS) sections.push('defaultBrowser'); else - sections = sections.concat(['internet', 'device']); + sections = sections.concat(['internet', 'bluetooth', 'device']); for (var i = 0; i < sections.length; i++) { var section = self.getSection(page, sections[i]);
diff --git a/chrome/test/data/webui/settings/bluetooth_page_browsertest_chromeos.js b/chrome/test/data/webui/settings/bluetooth_page_browsertest_chromeos.js deleted file mode 100644 index 3f7f6ffa3..0000000 --- a/chrome/test/data/webui/settings/bluetooth_page_browsertest_chromeos.js +++ /dev/null
@@ -1,198 +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. - -/** @fileoverview Suite of tests for settings-bluetooth-page. */ - -GEN_INCLUDE(['settings_page_browsertest.js']); - -var bluetoothPage = bluetoothPage || {}; - -/** - * @constructor - * @extends {SettingsPageBrowserTest} - */ -function SettingsBluetoothPageBrowserTest() { -} - -SettingsBluetoothPageBrowserTest.prototype = { - __proto__: SettingsPageBrowserTest.prototype, - - /** @override */ - extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ - '../fake_chrome_event.js', - 'fake_bluetooth.js', - 'fake_bluetooth_private.js' - ]), - - /** @type {Bluetooth} */ - bluetoothApi_: undefined, - - /** @type {BluetoothPrivate} */ - bluetoothPrivateApi_: undefined, - - /** @override */ - preLoad: function() { - SettingsPageBrowserTest.prototype.preLoad.call(this); - settingsHidePagesByDefaultForTest = true; - this.bluetoothApi_ = new settings.FakeBluetooth(); - this.bluetoothPrivateApi_ = - new settings.FakeBluetoothPrivate(this.bluetoothApi_); - // Set globals to override Settings Bluetooth Page apis. - bluetoothPage.bluetoothApiForTest = this.bluetoothApi_; - bluetoothPage.bluetoothPrivateApiForTest = this.bluetoothPrivateApi_; - } -}; - -// Times out on debug builders and may time out on memory bots because -// the Settings page can take several seconds to load in a Release build -// and several times that in a Debug build. See https://crbug.com/558434. -GEN('#if defined(MEMORY_SANITIZER) || !defined(NDEBUG)'); -GEN('#define MAYBE_Bluetooth DISABLED_Bluetooth'); -GEN('#else'); -GEN('#define MAYBE_Bluetooth Bluetooth'); -GEN('#endif'); - -// Runs bluetooth tests. -TEST_F('SettingsBluetoothPageBrowserTest', 'MAYBE_Bluetooth', function() { - // Differentiate |this| in the Test from |this| in suite() and test(), see - // comment at the top of mocha_adapter.js. - var self = this; - - self.toggleAdvanced(); - var page = self.getPage('basic'); - assertTrue(!!page); - page.set('pageVisibility.bluetooth', true); - Polymer.dom.flush(); - - /** @type {!Array<!chrome.bluetooth.Device>} */ var fakeDevices_ = [ - { - address: '10:00:00:00:00:01', - name: 'FakePairedDevice1', - paired: true, - connected: true, - }, - { - address: '10:00:00:00:00:02', - name: 'FakePairedDevice2', - paired: true, - connected: false, - connecting: true, - }, - { - address: '00:00:00:00:00:01', - name: 'FakeUnPairedDevice1', - paired: false, - }, - { - address: '00:00:00:00:00:02', - name: 'FakeUnPairedDevice2', - paired: false, - }, - ]; - - suite('SettingsBluetoothPage', function() { - test('enable', function() { - assertFalse(self.bluetoothApi_.adapterState.powered); - var bluetoothSection = self.getSection(page, 'bluetooth'); - assertTrue(!!bluetoothSection); - var bluetooth = - bluetoothSection.querySelector('settings-bluetooth-page'); - assertTrue(!!bluetooth); - expectFalse(bluetooth.bluetoothEnabled_); - var enable = bluetooth.$.enableBluetooth; - assertTrue(!!enable); - expectFalse(enable.checked); - // Test that tapping the enable checkbox changes its value and calls - // bluetooth.setAdapterState with powered = false. - MockInteractions.tap(enable); - Polymer.dom.flush(); - expectTrue(enable.checked); - expectTrue(self.bluetoothApi_.adapterState.powered); - // Confirm that 'bluetoothEnabled' remains set to true. - expectTrue(bluetooth.bluetoothEnabled_); - // Set 'bluetoothEnabled' directly and confirm that the checkbox - // toggles. - bluetooth.bluetoothEnabled_ = false; - Polymer.dom.flush(); - expectFalse(enable.checked); - }); - - test('device list', function() { - var bluetoothSection = self.getSection(page, 'bluetooth'); - var bluetooth = - bluetoothSection.querySelector('settings-bluetooth-page'); - assertTrue(!!bluetooth); - var deviceList = bluetooth.$.deviceList; - assertTrue(!!deviceList); - // Set enabled, with default (empty) device list. - self.bluetoothApi_.setEnabled(true); - Polymer.dom.flush(); - // Ensure that initially the 'no devices' element is visible. - expectFalse(deviceList.hidden); - var noDevices = deviceList.querySelector('.no-devices'); - assertTrue(!!noDevices); - expectFalse(noDevices.hidden); - // Set some devices (triggers onDeviceAdded events). 'no devices' element - // should be hidden. - self.bluetoothApi_.setDevicesForTest(fakeDevices_); - Polymer.dom.flush(); - assertEquals(bluetooth.deviceList_.length, 4); - var devicesIronList = bluetooth.$$('#deviceList iron-list'); - assertTrue(!!devicesIronList); - devicesIronList.notifyResize(); - Polymer.dom.flush(); - expectTrue(noDevices.hidden); - // Confirm that there are two paired devices in the list. - var devices = deviceList.querySelectorAll('bluetooth-device-list-item'); - assertEquals(2, devices.length); - // Check the state of each device. - assertTrue(devices[0].device.connected); - assertFalse(devices[1].device.connected); - assertTrue(devices[1].device.connecting); - }); - - test('device dialog', function() { - var bluetoothSection = self.getSection(page, 'bluetooth'); - var bluetooth = - bluetoothSection.querySelector('settings-bluetooth-page'); - assertTrue(!!bluetooth); - self.bluetoothApi_.setEnabled(true); - - // Tap the 'add device' button. - MockInteractions.tap(bluetooth.$$('.primary-button')); - Polymer.dom.flush(); - // Ensure the dialog appears. - var dialog = bluetooth.$$('#deviceDialog'); - assertTrue(!!dialog); - assertTrue(dialog.$.dialog.open); - assertEquals(dialog.deviceList.length, 4); - - // Ensure the dialog has the expected devices. - var devicesIronList = dialog.$$('#dialogDeviceList iron-list'); - assertTrue(!!devicesIronList); - devicesIronList.notifyResize(); - Polymer.dom.flush(); - var devices = - devicesIronList.querySelectorAll('bluetooth-device-list-item'); - assertEquals(devices.length, 2); - - // Select a device. - MockInteractions.tap(devices[0].$$('div')); - Polymer.dom.flush(); - // Ensure the pairing dialog is shown. - assertTrue(!!dialog.$$('#pairing')); - - // Ensure the device wass connected to. - expectEquals(1, self.bluetoothPrivateApi_.connectedDevices_.size); - - // Close the dialog. - dialog.close(); - Polymer.dom.flush(); - assertFalse(dialog.$.dialog.open); - }); - }); - - // Run all registered tests. - mocha.run(); -});
diff --git a/chrome/test/data/webui/settings/bluetooth_page_tests.js b/chrome/test/data/webui/settings/bluetooth_page_tests.js new file mode 100644 index 0000000..dd38476c --- /dev/null +++ b/chrome/test/data/webui/settings/bluetooth_page_tests.js
@@ -0,0 +1,170 @@ +// 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. + +suite('Bluetooth', function() { + var bluetoothPage = null; + + /** @type {Bluetooth} */ + var bluetoothApi_; + + /** @type {BluetoothPrivate} */ + var bluetoothPrivateApi_; + + /** @type {!Array<!chrome.bluetooth.Device>} */ var fakeDevices_ = [ + { + address: '10:00:00:00:00:01', + name: 'FakePairedDevice1', + paired: true, + connected: true, + }, + { + address: '10:00:00:00:00:02', + name: 'FakePairedDevice2', + paired: true, + connected: false, + connecting: true, + }, + { + address: '00:00:00:00:00:01', + name: 'FakeUnPairedDevice1', + paired: false, + }, + { + address: '00:00:00:00:00:02', + name: 'FakeUnPairedDevice2', + paired: false, + }, + ]; + + suiteSetup(function() { + window.loadTimeData = new LoadTimeData; + loadTimeData.data = { + bluetoothEnabled: 'bluetoothEnabled', + bluetoothDisabled: 'bluetoothDisabled', + bluetoothOn: 'bluetoothOn', + bluetoothOff: 'bluetoothOff', + bluetoothConnected: 'bluetoothConnected', + bluetoothDisconnect: 'bluetoothDisconnect', + bluetoothPair: 'bluetoothPair', + bluetoothStartConnecting: 'bluetoothStartConnecting', + + }; + + bluetoothApi_ = new settings.FakeBluetooth(); + bluetoothPrivateApi_ = new settings.FakeBluetoothPrivate(bluetoothApi_); + + // Set globals to override Settings Bluetooth Page apis. + bluetoothApis.bluetoothApiForTest = bluetoothApi_; + bluetoothApis.bluetoothPrivateApiForTest = bluetoothPrivateApi_; + + // Disable animations so sub-pages open within one event loop. + testing.Test.disableAnimationsAndTransitions(); + }); + + setup(function() { + PolymerTest.clearBody(); + bluetoothPage = document.createElement('settings-bluetooth-page'); + assertTrue(!!bluetoothPage); + + document.body.appendChild(bluetoothPage); + Polymer.dom.flush(); + }); + + teardown(function() { + bluetoothPage.remove(); + }); + + test('MainPage', function() { + assertFalse(bluetoothApi_.adapterState.powered); + assertFalse(bluetoothPage.bluetoothEnabled_); + // Test that tapping the single settings-box div enables bluetooth. + var div = bluetoothPage.$$('div.settings-box'); + assertTrue(!!div); + MockInteractions.tap(div); + assertTrue(bluetoothPage.bluetoothEnabled_); + assertTrue(bluetoothApi_.adapterState.powered); + }); + + suite('SubPage', function() { + var subpage; + + setup(function() { + bluetoothPage.bluetoothEnabled_ = true; + var div = bluetoothPage.$$('div.settings-box'); + MockInteractions.tap(div); + subpage = bluetoothPage.$$('settings-bluetooth-subpage'); + assertTrue(!!subpage); + }); + + test('toggle', function() { + assertTrue(subpage.bluetoothEnabled); + + var enableButton = subpage.$.enableBluetooth; + assertTrue(!!enableButton); + assertTrue(enableButton.checked); + + subpage.bluetoothEnabled = false; + assertFalse(enableButton.checked); + assertFalse(bluetoothApi_.adapterState.powered);; + assertFalse(bluetoothPage.bluetoothEnabled_); + }); + + test('device list', function() { + var deviceList = subpage.$.container; + assertTrue(!!deviceList); + assertTrue(deviceList.hidden); + assertFalse(subpage.$.noDevices.hidden); + + bluetoothApi_.setDevicesForTest(fakeDevices_); + Polymer.dom.flush(); + assertEquals(4, subpage.deviceList_.length); + assertTrue(subpage.$.noDevices.hidden); + + var devicesIronList = subpage.$$('#container > iron-list'); + assertTrue(!!devicesIronList); + devicesIronList.notifyResize(); + Polymer.dom.flush(); + var devices = deviceList.querySelectorAll('bluetooth-device-list-item'); + assertEquals(2, devices.length); + assertTrue(devices[0].device.connected); + assertFalse(devices[1].device.connected); + assertTrue(devices[1].device.connecting); + }); + + test('device dialog', function() { + // Tap the 'add device' button. + MockInteractions.tap(subpage.$.pairButton); + Polymer.dom.flush(); + + // Ensure the dialog appears. + var dialog = subpage.$.deviceDialog; + assertTrue(!!dialog); + assertTrue(dialog.$.dialog.open); + assertEquals(dialog.deviceList.length, 4); + + // Ensure the dialog has the expected devices. + var devicesIronList = dialog.$$('#dialogDeviceList iron-list'); + assertTrue(!!devicesIronList); + devicesIronList.notifyResize(); + Polymer.dom.flush(); + var devices = + devicesIronList.querySelectorAll('bluetooth-device-list-item'); + assertEquals(devices.length, 2); + + // Select a device. + MockInteractions.tap(devices[0].$$('div')); + Polymer.dom.flush(); + // Ensure the pairing dialog is shown. + assertTrue(!!dialog.$$('#pairing')); + + // Ensure the device wass connected to. + expectEquals(1, bluetoothPrivateApi_.connectedDevices_.size); + + // Close the dialog. + dialog.close(); + Polymer.dom.flush(); + assertFalse(dialog.$.dialog.open); + }); + }); +});
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js index 4a1f66e..b53e3053 100644 --- a/chrome/test/data/webui/settings/cr_settings_browsertest.js +++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -723,6 +723,35 @@ TEST_F('CrSettingsDevicePageTest', 'PowerTest', function() { mocha.grep(assert(device_page_tests.TestNames.Power)).run(); }); + +/** + * Test fixture for device-page. + * @constructor + * @extends {CrSettingsBrowserTest} + */ +function CrSettingsBluetoothPageTest() {} + +CrSettingsBluetoothPageTest.prototype = { + __proto__: CrSettingsBrowserTest.prototype, + + /** @override */ + browsePreload: 'chrome://md-settings/bluetooth_page/bluetooth_page.html', + + /** @override */ + extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([ + ROOT_PATH + 'ui/webui/resources/js/assert.js', + ROOT_PATH + 'ui/webui/resources/js/load_time_data.js', + '../fake_chrome_event.js', + 'fake_bluetooth.js', + 'fake_bluetooth_private.js', + 'bluetooth_page_tests.js', + ]), +}; + +TEST_F('CrSettingsBluetoothPageTest', 'BluetoothPageTest', function() { + mocha.run(); +}); + GEN('#endif'); /**
diff --git a/chrome/test/data/webui/settings/fake_bluetooth.js b/chrome/test/data/webui/settings/fake_bluetooth.js index b547c93..bda3e4d 100644 --- a/chrome/test/data/webui/settings/fake_bluetooth.js +++ b/chrome/test/data/webui/settings/fake_bluetooth.js
@@ -47,9 +47,7 @@ // Bluetooth overrides. /** @override */ getAdapterState: function(callback) { - setTimeout(function() { - callback(this.adapterState); - }.bind(this)); + callback(this.adapterState); }, /** @override */ @@ -57,9 +55,7 @@ /** @override */ getDevices: function(callback) { - setTimeout(function() { - callback(this.devices); - }.bind(this)); + callback(this.devices); }, /** @override */
diff --git a/chrome/test/data/webui/settings/settings_subpage_browsertest.js b/chrome/test/data/webui/settings/settings_subpage_browsertest.js index 1e0e804..5e32e64 100644 --- a/chrome/test/data/webui/settings/settings_subpage_browsertest.js +++ b/chrome/test/data/webui/settings/settings_subpage_browsertest.js
@@ -66,13 +66,13 @@ */ testSubPage: function(page, subPage) { Polymer.dom.flush(); - expectFalse(!!this.getSection(page, subPage)); + assertFalse(!!this.getSection(page, subPage)); var startTime = window.performance.now(); page.set('pageVisibility.' + subPage, true); Polymer.dom.flush(); var dtime = window.performance.now() - startTime; console.log('Page: ' + subPage + ' Load time: ' + dtime.toFixed(0) + ' ms'); - expectTrue(!!this.getSection(page, subPage)); + assertTrue(!!this.getSection(page, subPage)); // Hide the page so that it doesn't interfere with other subPages. page.set('pageVisibility.' + subPage, false); Polymer.dom.flush(); @@ -98,7 +98,7 @@ 'search', ]; if (cr.isChromeOS) - this.subPages.push('device', 'internet'); + this.subPages.push('internet', 'bluetooth', 'device'); else this.subPages.push('defaultBrowser'); } @@ -128,7 +128,7 @@ 'reset', ]; if (cr.isChromeOS) - this.subPages.push('dateTime', 'bluetooth'); + this.subPages.push('dateTime'); else this.subPages.push('system'); };
diff --git a/components/arc/arc_features.cc b/components/arc/arc_features.cc index 5e3f68ff..e5db91b 100644 --- a/components/arc/arc_features.cc +++ b/components/arc/arc_features.cc
@@ -17,4 +17,9 @@ "ArcBootCompletedBroadcast", base::FEATURE_ENABLED_BY_DEFAULT }; +// Controls whether we show Arc Files app in Chrome launcher. +const base::Feature kShowArcFilesAppFeature { + "ShowArcFilesApp", base::FEATURE_DISABLED_BY_DEFAULT +}; + } // namespace arc
diff --git a/components/arc/arc_features.h b/components/arc/arc_features.h index 3bb070a..52dec01 100644 --- a/components/arc/arc_features.h +++ b/components/arc/arc_features.h
@@ -14,6 +14,7 @@ // Please keep alphabetized. extern const base::Feature kArcUseAuthEndpointFeature; extern const base::Feature kBootCompletedBroadcastFeature; +extern const base::Feature kShowArcFilesAppFeature; } // namespace arc
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc index 9bbdd135..69a16f4 100644 --- a/components/autofill/content/renderer/password_autofill_agent.cc +++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -853,12 +853,10 @@ if ((element.isPasswordField() || HasAutocompleteAttributeValue(element, "username")) && security_state::IsHttpWarningInFormEnabled() && - !content::IsOriginSecure(url::Origin(render_frame() - ->GetRenderView() - ->GetMainRenderFrame() - ->GetWebFrame() - ->getSecurityOrigin()) - .GetURL())) { + !content::IsOriginSecure( + url::Origin( + render_frame()->GetWebFrame()->top()->getSecurityOrigin()) + .GetURL())) { autofill_agent_->ShowNotSecureWarning(element); return true; }
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc index 09d746b..815c7424 100644 --- a/components/autofill/core/browser/autofill_manager.cc +++ b/components/autofill/core/browser/autofill_manager.cc
@@ -1167,6 +1167,13 @@ upload_request_ = payments::PaymentsClient::UploadRequestDetails(); upload_request_.card = *imported_credit_card; + // In order to prompt the user to upload their card, we must have both: + // 1) Card with CVC + // 2) 1+ recently-used or modified addresses that meet the client-side + // validation rules + // Here we perform both checks before returning or logging anything, + // because if only one of the two is missing, it may be fixable. + // Check for a CVC to determine whether we can prompt the user to upload // their card. If no CVC is present, do nothing. We could fall back to a // local save but we believe that sometimes offering upload and sometimes @@ -1179,6 +1186,20 @@ break; } } + + // Upload requires that recently used or modified addresses meet the + // client-side validation rules. + autofill::AutofillMetrics::CardUploadDecisionMetric + get_profiles_decision_metric = AutofillMetrics::UPLOAD_OFFERED; + std::string rappor_metric_name; + bool get_profiles_succeeded = + GetProfilesForCreditCardUpload(*imported_credit_card, + &upload_request_.profiles, + &get_profiles_decision_metric, + &rappor_metric_name); + + // Both the CVC and address checks are done. Conform to the legacy order of + // reporting on CVC then address. if (upload_request_.cvc.empty()) { AutofillMetrics::LogCardUploadDecisionMetric( AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); @@ -1186,12 +1207,13 @@ "Autofill.CardUploadNotOfferedNoCvc"); return; } - - // Upload also requires recently used or modified addresses that meet the - // client-side validation rules. - if (!GetProfilesForCreditCardUpload(*imported_credit_card, - &upload_request_.profiles, - submitted_form.source_url())) { + if (!get_profiles_succeeded) { + DCHECK(get_profiles_decision_metric != AutofillMetrics::UPLOAD_OFFERED); + AutofillMetrics::LogCardUploadDecisionMetric( + get_profiles_decision_metric); + if (!rappor_metric_name.empty()) { + CollectRapportSample(submitted_form.source_url(), rappor_metric_name); + } return; } @@ -1203,7 +1225,9 @@ bool AutofillManager::GetProfilesForCreditCardUpload( const CreditCard& card, std::vector<AutofillProfile>* profiles, - const GURL& source_url) const { + autofill::AutofillMetrics::CardUploadDecisionMetric* + address_upload_decision_metric, + std::string* rappor_metric_name) const { std::vector<AutofillProfile> candidate_profiles; const base::Time now = base::Time::Now(); const base::TimeDelta fifteen_minutes = base::TimeDelta::FromMinutes(15); @@ -1216,9 +1240,9 @@ } } if (candidate_profiles.empty()) { - AutofillMetrics::LogCardUploadDecisionMetric( - AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS); - CollectRapportSample(source_url, "Autofill.CardUploadNotOfferedNoAddress"); + *address_upload_decision_metric = + AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS; + *rappor_metric_name = "Autofill.CardUploadNotOfferedNoAddress"; return false; } @@ -1246,10 +1270,9 @@ // countries, we'll need to make the name comparison more sophisticated. if (!base::EqualsCaseInsensitiveASCII( verified_name, RemoveMiddleInitial(address_name))) { - AutofillMetrics::LogCardUploadDecisionMetric( - AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES); - CollectRapportSample(source_url, - "Autofill.CardUploadNotOfferedConflictingNames"); + *address_upload_decision_metric = + AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES; + *rappor_metric_name = "Autofill.CardUploadNotOfferedConflictingNames"; return false; } } @@ -1258,9 +1281,9 @@ // If neither the card nor any of the addresses have a name associated with // them, the candidate set is invalid. if (verified_name.empty()) { - AutofillMetrics::LogCardUploadDecisionMetric( - AutofillMetrics::UPLOAD_NOT_OFFERED_NO_NAME); - CollectRapportSample(source_url, "Autofill.CardUploadNotOfferedNoName"); + *address_upload_decision_metric = + AutofillMetrics::UPLOAD_NOT_OFFERED_NO_NAME; + *rappor_metric_name = "Autofill.CardUploadNotOfferedNoName"; return false; } @@ -1286,8 +1309,8 @@ // likely to fail. if (!(StartsWith(verified_zip, zip, base::CompareCase::SENSITIVE) || StartsWith(zip, verified_zip, base::CompareCase::SENSITIVE))) { - AutofillMetrics::LogCardUploadDecisionMetric( - AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_ZIPS); + *address_upload_decision_metric = + AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_ZIPS; return false; } } @@ -1297,8 +1320,8 @@ // If none of the candidate addresses have a zip, the candidate set is // invalid. if (verified_zip.empty()) { - AutofillMetrics::LogCardUploadDecisionMetric( - AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ZIP_CODE); + *address_upload_decision_metric = + AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ZIP_CODE; return false; } @@ -1307,7 +1330,8 @@ } void AutofillManager::CollectRapportSample(const GURL& source_url, - const char* metric_name) const { + const std::string& metric_name) + const { if (source_url.is_valid() && client_->GetRapporServiceImpl()) { rappor::SampleDomainAndRegistryFromGURL(client_->GetRapporServiceImpl(), metric_name, source_url);
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h index 5fe91c8..d445d38 100644 --- a/components/autofill/core/browser/autofill_manager.h +++ b/components/autofill/core/browser/autofill_manager.h
@@ -404,15 +404,19 @@ // Logs |metric_name| with RAPPOR, for the specific form |source_url|. void CollectRapportSample(const GURL& source_url, - const char* metric_name) const; + const std::string& metric_name) const; // Examines |card| and the stored profiles and if a candidate set of profiles // is found that matches the client-side validation rules, assigns the values - // to |profiles|. |source_url| is the source URL for the form. If no valid set - // can be found, returns false. + // to |profiles|. If no valid set can be found, returns false, assigns the + // failure reason to |address_upload_decision_metric|, and if applicable, the + // RAPPOR metric to log to |rappor_metric_name|. bool GetProfilesForCreditCardUpload(const CreditCard& card, std::vector<AutofillProfile>* profiles, - const GURL& source_url) const; + autofill::AutofillMetrics:: + CardUploadDecisionMetric* + address_upload_decision_metric, + std::string* rappor_metric_name) const; // If |initial_interaction_timestamp_| is unset or is set to a later time than // |interaction_timestamp|, updates the cached timestamp. The latter check is
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc index 3b85c34a..7442b0b 100644 --- a/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -4762,6 +4762,56 @@ // TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. #if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_CvcUnavailableAndNoProfileAvailable DISABLED_UploadCreditCard_CvcUnavailableAndNoProfileAvailable +#else +#define MAYBE_UploadCreditCard_CvcUnavailableAndNoProfileAvailable UploadCreditCard_CvcUnavailableAndNoProfileAvailable +#endif +TEST_F(AutofillManagerTest, + MAYBE_UploadCreditCard_CvcUnavailableAndNoProfileAvailable) { + personal_data_.ClearAutofillProfiles(); + autofill_manager_->set_credit_card_upload_enabled(true); + + // Don't fill or submit an address form. + + // Set up our credit card form data. + FormData credit_card_form; + CreateTestCreditCardFormData(&credit_card_form, true, false); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + + // Edit the data, and submit. + credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); + credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); + credit_card_form.fields[2].value = ASCIIToUTF16("11"); + credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[4].value = ASCIIToUTF16(""); // CVC MISSING + + base::HistogramTester histogram_tester; + + // Neither a local save nor an upload should happen in this case. + // Note that AutofillManager should *check* for both no CVC and no address + // profile, but the no CVC case should have priority over being reported. + EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0); + FormSubmitted(credit_card_form); + EXPECT_FALSE(autofill_manager_->credit_card_was_uploaded()); + + // Verify that the correct histogram entry (and only that) was logged. + histogram_tester.ExpectUniqueSample( + "Autofill.CardUploadDecisionExpanded", + AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC, 1); + + rappor::TestRapporServiceImpl* rappor_service = + autofill_client_.test_rappor_service(); + EXPECT_EQ(1, rappor_service->GetReportsCount()); + std::string sample; + rappor::RapporType type; + EXPECT_TRUE(rappor_service->GetRecordedSampleForMetric( + "Autofill.CardUploadNotOfferedNoCvc", &sample, &type)); + EXPECT_EQ("myform.com", sample); + EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type); +} + +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) #define MAYBE_UploadCreditCard_NoNameAvailable DISABLED_UploadCreditCard_NoNameAvailable #else #define MAYBE_UploadCreditCard_NoNameAvailable UploadCreditCard_NoNameAvailable
diff --git a/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc b/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc index 0bf47d3..c5ce09b 100644 --- a/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc +++ b/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc
@@ -143,17 +143,19 @@ return ModelError(FROM_HERE, "Failed reading from WebDatabase."); } else if (!local) { save_to_local_.push_back(remote); - } else if (remote != local.value()) { - if (specifics.usage_timestamp().empty()) { - // Skip merging if there are no timestamps. We don't want to wipe out - // a local value of |date_created| if the remote copy is oddly formed. - save_to_sync_.push_back(local.value()); - } else { - const AutofillEntry merged = MergeEntryDates(local.value(), remote); - save_to_local_.push_back(merged); - save_to_sync_.push_back(merged); - } + } else { unique_to_local_.erase(local.value()); + if (remote != local.value()) { + if (specifics.usage_timestamp().empty()) { + // Skip merging if there are no timestamps. We don't want to wipe out + // a local value of |date_created| if the remote copy is oddly formed. + save_to_sync_.push_back(local.value()); + } else { + const AutofillEntry merged = MergeEntryDates(local.value(), remote); + save_to_local_.push_back(merged); + save_to_sync_.push_back(merged); + } + } } return {}; } @@ -318,8 +320,7 @@ EntityDataMap entity_data_map) { DCHECK(thread_checker_.CalledOnValidThread()); - // TODO(skym, crbug.com/680218): Uncomment and add unit tests. - /*SyncDifferenceTracker tracker(GetAutofillTable()); + SyncDifferenceTracker tracker(GetAutofillTable()); for (auto kv : entity_data_map) { DCHECK(kv.second->specifics.has_autofill()); RETURN_IF_ERROR(tracker.IncorporateRemoteSpecifics( @@ -330,7 +331,7 @@ RETURN_IF_ERROR(tracker.FlushToSync(true, std::move(metadata_change_list), change_processor())); web_data_backend_->RemoveExpiredFormElements(); - web_data_backend_->NotifyThatSyncHasStarted(syncer::AUTOFILL);*/ + web_data_backend_->NotifyThatSyncHasStarted(syncer::AUTOFILL); return {}; }
diff --git a/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc index b12d0ef..104f0559 100644 --- a/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc +++ b/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
@@ -6,6 +6,7 @@ #include <algorithm> #include <map> +#include <utility> #include <vector> #include "base/bind.h" @@ -35,10 +36,11 @@ using sync_pb::AutofillSpecifics; using sync_pb::EntitySpecifics; using syncer::DataBatch; -using syncer::EntityData; -using syncer::EntityDataPtr; using syncer::EntityChange; using syncer::EntityChangeList; +using syncer::EntityData; +using syncer::EntityDataPtr; +using syncer::EntityDataMap; using syncer::FakeModelTypeChangeProcessor; using syncer::KeyAndData; using syncer::ModelError; @@ -177,6 +179,13 @@ return CreateSpecifics(suffix, std::vector<int>{0}); } + std::string GetClientTag(const AutofillSpecifics& specifics) { + std::string tag = + bridge()->GetClientTag(SpecificsToEntity(specifics).value()); + EXPECT_FALSE(tag.empty()); + return tag; + } + std::string GetStorageKey(const AutofillSpecifics& specifics) { std::string key = bridge()->GetStorageKey(SpecificsToEntity(specifics).value()); @@ -194,6 +203,15 @@ return changes; } + EntityDataMap CreateEntityDataMap( + const std::vector<AutofillSpecifics>& specifics_vector) { + EntityDataMap map; + for (const auto& specifics : specifics_vector) { + map[GetStorageKey(specifics)] = SpecificsToEntity(specifics); + } + return map; + } + void VerifyApplyChanges(const std::vector<EntityChange>& changes) { const auto error = bridge()->ApplySyncChanges( bridge()->CreateMetadataChangeList(), changes); @@ -204,6 +222,12 @@ VerifyApplyChanges(EntityAddList(specifics)); } + void VerifyMerge(const std::vector<AutofillSpecifics>& specifics) { + const auto error = bridge()->MergeSyncData( + bridge()->CreateMetadataChangeList(), CreateEntityDataMap(specifics)); + EXPECT_FALSE(error); + } + std::map<std::string, AutofillSpecifics> ExpectedMap( const std::vector<AutofillSpecifics>& specifics_vector) { std::map<std::string, AutofillSpecifics> map; @@ -265,7 +289,50 @@ }; TEST_F(AutocompleteSyncBridgeTest, GetClientTag) { - // TODO(skym, crbug.com/675991): Implementation. + std::string tag = GetClientTag(CreateSpecifics(1)); + EXPECT_EQ(tag, GetClientTag(CreateSpecifics(1))); + EXPECT_NE(tag, GetClientTag(CreateSpecifics(2))); +} + +TEST_F(AutocompleteSyncBridgeTest, GetClientTagNotAffectedByTimestamp) { + AutofillSpecifics specifics = CreateSpecifics(1); + std::string tag = GetClientTag(specifics); + + specifics.add_usage_timestamp(1); + EXPECT_EQ(tag, GetClientTag(specifics)); + + specifics.add_usage_timestamp(0); + EXPECT_EQ(tag, GetClientTag(specifics)); + + specifics.add_usage_timestamp(-1); + EXPECT_EQ(tag, GetClientTag(specifics)); +} + +TEST_F(AutocompleteSyncBridgeTest, GetClientTagRespectsNullCharacter) { + AutofillSpecifics specifics; + std::string tag = GetClientTag(specifics); + + specifics.set_value(std::string("\0", 1)); + EXPECT_NE(tag, GetClientTag(specifics)); +} + +// The client tags should never change as long as we want to maintain backwards +// compatibility with the previous iteration of autocomplete-sync integration, +// AutocompleteSyncableService and Sync's Directory. This is because old clients +// will re-generate client tags and then hashes on local changes, and this +// process must create identical values to what this client has created. If this +// test case starts failing, you should not alter the fixed values here unless +// you know what you're doing. +TEST_F(AutocompleteSyncBridgeTest, GetClientTagFixed) { + EXPECT_EQ("autofill_entry|name%201|value%201", + GetClientTag(CreateSpecifics(1))); + EXPECT_EQ("autofill_entry|name%202|value%202", + GetClientTag(CreateSpecifics(2))); + EXPECT_EQ("autofill_entry||", GetClientTag(AutofillSpecifics())); + AutofillSpecifics specifics; + specifics.set_name("\xEC\xA4\x91"); + specifics.set_value("\xD0\x80"); + EXPECT_EQ("autofill_entry|%EC%A4%91|%D0%80", GetClientTag(specifics)); } TEST_F(AutocompleteSyncBridgeTest, GetStorageKey) { @@ -274,8 +341,7 @@ EXPECT_NE(key, GetStorageKey(CreateSpecifics(2))); } -// Timestamps should not affect storage keys. -TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyTimestamp) { +TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyNotAffectedByTimestamp) { AutofillSpecifics specifics = CreateSpecifics(1); std::string key = GetStorageKey(specifics); @@ -289,8 +355,7 @@ EXPECT_EQ(key, GetStorageKey(specifics)); } -// Verify that the \0 character is respected as a difference. -TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyNull) { +TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyRespectsNullCharacter) { AutofillSpecifics specifics; std::string key = GetStorageKey(specifics); @@ -521,4 +586,89 @@ EXPECT_TRUE(processor()->metadata()->GetModelTypeState().initial_sync_done()); } +TEST_F(AutocompleteSyncBridgeTest, MergeSyncDataEmpty) { + VerifyMerge(std::vector<AutofillSpecifics>()); + + VerifyAllData(std::vector<AutofillSpecifics>()); + EXPECT_EQ(0u, processor()->delete_set().size()); + EXPECT_EQ(0u, processor()->put_multimap().size()); +} + +TEST_F(AutocompleteSyncBridgeTest, MergeSyncDataRemoteOnly) { + const AutofillSpecifics specifics1 = CreateSpecifics(1, {2}); + const AutofillSpecifics specifics2 = CreateSpecifics(2, {3, 4}); + + VerifyMerge({specifics1, specifics2}); + + VerifyAllData({specifics1, specifics2}); + EXPECT_EQ(0u, processor()->delete_set().size()); + EXPECT_EQ(0u, processor()->put_multimap().size()); +} + +TEST_F(AutocompleteSyncBridgeTest, MergeSyncDataLocalOnly) { + const AutofillSpecifics specifics1 = CreateSpecifics(1, {2}); + const AutofillSpecifics specifics2 = CreateSpecifics(2, {3, 4}); + VerifyApplyAdds({specifics1, specifics2}); + VerifyAllData({specifics1, specifics2}); + + VerifyMerge(std::vector<AutofillSpecifics>()); + + VerifyAllData({specifics1, specifics2}); + EXPECT_EQ(2u, processor()->put_multimap().size()); + VerifyProcessorRecordedPut(specifics1); + VerifyProcessorRecordedPut(specifics2); + EXPECT_EQ(0u, processor()->delete_set().size()); +} + +TEST_F(AutocompleteSyncBridgeTest, MergeSyncDataAllMerged) { + const AutofillSpecifics local1 = CreateSpecifics(1, {2}); + const AutofillSpecifics local2 = CreateSpecifics(2, {3, 4}); + const AutofillSpecifics local3 = CreateSpecifics(3, {4}); + const AutofillSpecifics local4 = CreateSpecifics(4, {5, 6}); + const AutofillSpecifics local5 = CreateSpecifics(5, {6, 9}); + const AutofillSpecifics local6 = CreateSpecifics(6, {7, 9}); + const AutofillSpecifics remote1 = local1; + const AutofillSpecifics remote2 = local2; + const AutofillSpecifics remote3 = CreateSpecifics(3, {5}); + const AutofillSpecifics remote4 = CreateSpecifics(4, {7, 8}); + const AutofillSpecifics remote5 = CreateSpecifics(5, {8, 9}); + const AutofillSpecifics remote6 = CreateSpecifics(6, {8, 10}); + const AutofillSpecifics merged1 = local1; + const AutofillSpecifics merged2 = local2; + const AutofillSpecifics merged3 = CreateSpecifics(3, {4, 5}); + const AutofillSpecifics merged4 = CreateSpecifics(4, {5, 8}); + const AutofillSpecifics merged5 = local5; + const AutofillSpecifics merged6 = CreateSpecifics(6, {7, 10}); + VerifyApplyAdds({local1, local2, local3, local4, local5, local6}); + + VerifyMerge({remote1, remote2, remote3, remote4, remote5, remote6}); + + VerifyAllData({merged1, merged2, merged3, merged4, merged5, merged6}); + EXPECT_EQ(4u, processor()->put_multimap().size()); + VerifyProcessorRecordedPut(merged3); + VerifyProcessorRecordedPut(merged4); + VerifyProcessorRecordedPut(merged5); + VerifyProcessorRecordedPut(merged6); + EXPECT_EQ(0u, processor()->delete_set().size()); +} + +TEST_F(AutocompleteSyncBridgeTest, MergeSyncDataMixed) { + const AutofillSpecifics local1 = CreateSpecifics(1, {2, 3}); + const AutofillSpecifics remote2 = CreateSpecifics(2, {2, 3}); + const AutofillSpecifics specifics3 = CreateSpecifics(3, {2, 3}); + const AutofillSpecifics local4 = CreateSpecifics(4, {1, 3}); + const AutofillSpecifics remote4 = CreateSpecifics(4, {2, 4}); + const AutofillSpecifics merged4 = CreateSpecifics(4, {1, 4}); + + VerifyApplyAdds({local1, specifics3, local4}); + + VerifyMerge({remote2, specifics3, remote4}); + + VerifyAllData({local1, remote2, specifics3, merged4}); + EXPECT_EQ(2u, processor()->put_multimap().size()); + VerifyProcessorRecordedPut(local1); + VerifyProcessorRecordedPut(merged4); + EXPECT_EQ(0u, processor()->delete_set().size()); +} + } // namespace autofill
diff --git a/components/components_google_chrome_strings.grd b/components/components_google_chrome_strings.grd index 256a044..4033f6e 100644 --- a/components/components_google_chrome_strings.grd +++ b/components/components_google_chrome_strings.grd
@@ -222,7 +222,7 @@ This page includes a password or credit card input over HTTP. A warning will appear in the URL bar starting in Chrome 56 (Jan 2017). </message> <message name="IDS_PRIVATE_USER_DATA_INPUT_DESCRIPTION" desc="Description of a security problem where the site collects private user data on an insecure page, which results in an omnibox warning." translateable="false"> - This page includes a password or credit card input over HTTP. A warning has been added the URL bar. + This page includes a password or credit card input over HTTP. A warning has been added to the URL bar. </message> </messages> </release>
diff --git a/components/metrics/net/network_metrics_provider.cc b/components/metrics/net/network_metrics_provider.cc index bf56530..cd2f4f5 100644 --- a/components/metrics/net/network_metrics_provider.cc +++ b/components/metrics/net/network_metrics_provider.cc
@@ -18,6 +18,7 @@ #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/task_runner_util.h" +#include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" #include "net/base/net_errors.h" #include "net/nqe/network_quality_estimator.h"
diff --git a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc index c612b36..27cfb98 100644 --- a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc +++ b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc
@@ -152,11 +152,13 @@ const std::set<std::string>& known_suggestion_ids, const FetchDoneCallback& callback) { DCHECK_EQ(category, provided_category_); + std::vector<ContentSuggestion> suggestions = + GetMostRecentPhysicalWebPagesWithFilter(kMaxSuggestionsCount, + known_suggestion_ids); + AppendToShownScannedUrls(suggestions); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(callback, Status::Success(), - base::Passed(GetMostRecentPhysicalWebPagesWithFilter( - kMaxSuggestionsCount, known_suggestion_ids)))); + FROM_HERE, base::Bind(callback, Status::Success(), + base::Passed(std::move(suggestions)))); } void PhysicalWebPageSuggestionsProvider::ClearHistory( @@ -215,21 +217,25 @@ void PhysicalWebPageSuggestionsProvider::FetchPhysicalWebPages() { DCHECK_EQ(CategoryStatus::AVAILABLE, category_status_); + std::vector<ContentSuggestion> suggestions = + GetMostRecentPhysicalWebPagesWithFilter( + kMaxSuggestionsCount, + /*excluded_ids=*/std::set<std::string>()); + shown_resolved_urls_by_scanned_url_.clear(); + AppendToShownScannedUrls(suggestions); observer()->OnNewSuggestions(this, provided_category_, - GetMostRecentPhysicalWebPagesWithFilter( - kMaxSuggestionsCount, - /*excluded_ids=*/std::set<std::string>())); + std::move(suggestions)); } std::vector<ContentSuggestion> PhysicalWebPageSuggestionsProvider::GetMostRecentPhysicalWebPagesWithFilter( - int max_quantity, + int max_count, const std::set<std::string>& excluded_ids) { std::unique_ptr<physical_web::MetadataList> page_metadata_list = physical_web_data_source_->GetMetadataList(); // These is to filter out dismissed suggestions and at the same time prune the - // dismissed IDs list removing nonavailable pages (this is need since some + // dismissed IDs list removing nonavailable pages (this is needed since some // OnLost() calls may have been missed). const std::set<std::string> old_dismissed_ids = ReadDismissedIDsFromPrefs(); std::set<std::string> new_dismissed_ids; @@ -256,7 +262,7 @@ std::vector<ContentSuggestion> suggestions; for (const auto& page_metadata : filtered_metadata_list) { - if (static_cast<int>(suggestions.size()) == max_quantity) { + if (static_cast<int>(suggestions.size()) == max_count) { break; } suggestions.push_back(ConvertPhysicalWebPage(page_metadata)); @@ -283,7 +289,33 @@ } void PhysicalWebPageSuggestionsProvider::OnLost(const GURL& url) { - InvalidateSuggestion(url.spec()); + auto it = shown_resolved_urls_by_scanned_url_.find(url); + if (it == shown_resolved_urls_by_scanned_url_.end()) { + // The notification is propagated further in case the suggestion is shown on + // old NTPs (created before last |shown_resolved_urls_by_scanned_url_| + // update). + + // TODO(vitaliii): Use |resolved_url| here when it is available. Currently + // there is no way to find out |resolved_url|, which corresponds to this + // |scanned_url| (the metadata has been already removed from the Physical + // Web list). We use |scanned_url| (it may be the same as |resolved_url|, + // otherwise nothing happens), however, we should use the latter once it is + // provided (e.g. as an argument). + InvalidateSuggestion(url.spec()); + return; + } + + // This is not a reference, because the multimap pair will be removed below. + const GURL lost_resolved_url = it->second; + shown_resolved_urls_by_scanned_url_.erase(it); + if (std::find_if(shown_resolved_urls_by_scanned_url_.begin(), + shown_resolved_urls_by_scanned_url_.end(), + [lost_resolved_url](const std::pair<GURL, GURL>& pair) { + return lost_resolved_url == pair.second; + }) == shown_resolved_urls_by_scanned_url_.end()) { + // There are no more beacons for this URL. + InvalidateSuggestion(lost_resolved_url.spec()); + } } void PhysicalWebPageSuggestionsProvider::OnDistanceChanged( @@ -306,6 +338,21 @@ } } +void PhysicalWebPageSuggestionsProvider::AppendToShownScannedUrls( + const std::vector<ContentSuggestion>& suggestions) { + std::unique_ptr<physical_web::MetadataList> page_metadata_list = + physical_web_data_source_->GetMetadataList(); + for (const auto& page_metadata : *page_metadata_list) { + if (std::find_if(suggestions.begin(), suggestions.end(), + [page_metadata](const ContentSuggestion& suggestion) { + return suggestion.url() == page_metadata.resolved_url; + }) != suggestions.end()) { + shown_resolved_urls_by_scanned_url_.insert(std::make_pair( + page_metadata.scanned_url, page_metadata.resolved_url)); + } + } +} + std::set<std::string> PhysicalWebPageSuggestionsProvider::ReadDismissedIDsFromPrefs() const { return prefs::ReadDismissedIDsFromPrefs(
diff --git a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h index 67661d6c..befc8ce5 100644 --- a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h +++ b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h
@@ -5,6 +5,7 @@ #ifndef COMPONENTS_NTP_SNIPPETS_PHYSICAL_WEB_PAGES_PHYSICAL_WEB_PAGE_SUGGESTIONS_PROVIDER_H_ #define COMPONENTS_NTP_SNIPPETS_PHYSICAL_WEB_PAGES_PHYSICAL_WEB_PAGE_SUGGESTIONS_PROVIDER_H_ +#include <map> #include <set> #include <vector> @@ -90,12 +91,16 @@ // necessary. void InvalidateSuggestion(const std::string& page_id); + void AppendToShownScannedUrls( + const std::vector<ContentSuggestion>& suggestions); + // Reads dismissed IDs from Prefs. std::set<std::string> ReadDismissedIDsFromPrefs() const; // Writes |dismissed_ids| into Prefs. void StoreDismissedIDsToPrefs(const std::set<std::string>& dismissed_ids); + std::multimap<GURL, GURL> shown_resolved_urls_by_scanned_url_; CategoryStatus category_status_; const Category provided_category_; physical_web::PhysicalWebDataSource* physical_web_data_source_;
diff --git a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider_unittest.cc b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider_unittest.cc index 5c91ddc3..696cebb5 100644 --- a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider_unittest.cc +++ b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider_unittest.cc
@@ -298,17 +298,6 @@ } TEST_F(PhysicalWebPageSuggestionsProviderTest, - ShouldInvalidateSuggestionOnUrlLost) { - IgnoreOnCategoryStatusChangedToAvailable(); - IgnoreOnNewSuggestions(); - physical_web_data_source()->SetMetadataList(CreateDummyPhysicalWebPages({1})); - CreateProvider(); - - EXPECT_CALL(*observer(), OnSuggestionInvalidated(_, GetDummySuggestionId(1))); - FireUrlLost("https://resolved_url.com/1"); -} - -TEST_F(PhysicalWebPageSuggestionsProviderTest, ShouldNotShowDismissedSuggestions) { IgnoreOnCategoryStatusChangedToAvailable(); IgnoreOnSuggestionInvalidated(); @@ -405,4 +394,75 @@ ElementsAre(HasUrl("https://resolved_url.com/1"))); } +TEST_F(PhysicalWebPageSuggestionsProviderTest, + ShouldInvalidateSuggestionWhenItsOnlyBeaconIsLost) { + IgnoreOnCategoryStatusChangedToAvailable(); + physical_web_data_source()->SetMetadataList( + CreateDummyPhysicalWebPages({1, 2})); + + EXPECT_CALL(*observer(), OnNewSuggestions(_, provided_category(), _)); + CreateProvider(); + + EXPECT_CALL(*observer(), OnSuggestionInvalidated(_, GetDummySuggestionId(1))); + FireUrlLost("https://scanned_url.com/1"); +} + +TEST_F(PhysicalWebPageSuggestionsProviderTest, + ShouldNotInvalidateSuggestionWhenBeaconWithDifferentScannedURLRemains) { + IgnoreOnCategoryStatusChangedToAvailable(); + // Make 2 beacons point to the same URL, while having different |scanned_url|. + std::unique_ptr<physical_web::MetadataList> pages = + CreateDummyPhysicalWebPages({1, 2}); + (*pages)[1].resolved_url = (*pages)[0].resolved_url; + physical_web_data_source()->SetMetadataList(std::move(pages)); + + EXPECT_CALL(*observer(), OnNewSuggestions(_, provided_category(), _)); + CreateProvider(); + + // The first beacons is lost, but the second one still points to the same + // |resolved_url|, so the suggestion must not be invalidated. + EXPECT_CALL(*observer(), OnSuggestionInvalidated(_, _)).Times(0); + FireUrlLost("https://scanned_url.com/1"); +} + +TEST_F(PhysicalWebPageSuggestionsProviderTest, + ShouldNotInvalidateSuggestionWhenBeaconWithSameScannedURLRemains) { + IgnoreOnCategoryStatusChangedToAvailable(); + // Make 2 beacons point to the same URL, while having the same |scanned_url|. + std::unique_ptr<physical_web::MetadataList> pages = + CreateDummyPhysicalWebPages({1, 2}); + (*pages)[1].scanned_url = (*pages)[0].scanned_url; + (*pages)[1].resolved_url = (*pages)[0].resolved_url; + physical_web_data_source()->SetMetadataList(std::move(pages)); + + EXPECT_CALL(*observer(), OnNewSuggestions(_, provided_category(), _)); + CreateProvider(); + + // The first beacons is lost, but the second one still points to the same + // |resolved_url|, so the suggestion must not be invalidated. + EXPECT_CALL(*observer(), OnSuggestionInvalidated(_, _)).Times(0); + FireUrlLost("https://scanned_url.com/1"); +} + +TEST_F(PhysicalWebPageSuggestionsProviderTest, + ShouldInvalidateSuggestionWhenAllBeaconsLost) { + IgnoreOnCategoryStatusChangedToAvailable(); + // Make 3 beacons point to the same URL. Two of them have the same + // |scanned_url|. + std::unique_ptr<physical_web::MetadataList> pages = + CreateDummyPhysicalWebPages({1, 2, 3}); + (*pages)[1].scanned_url = (*pages)[0].scanned_url; + (*pages)[1].resolved_url = (*pages)[0].resolved_url; + (*pages)[2].resolved_url = (*pages)[0].resolved_url; + physical_web_data_source()->SetMetadataList(std::move(pages)); + + EXPECT_CALL(*observer(), OnNewSuggestions(_, provided_category(), _)); + CreateProvider(); + + FireUrlLost("https://scanned_url.com/1"); + FireUrlLost("https://scanned_url.com/1"); + EXPECT_CALL(*observer(), OnSuggestionInvalidated(_, GetDummySuggestionId(1))); + FireUrlLost("https://scanned_url.com/3"); +} + } // namespace ntp_snippets
diff --git a/components/ntp_tiles/most_visited_sites_unittest.cc b/components/ntp_tiles/most_visited_sites_unittest.cc index 9c16609..d660682b 100644 --- a/components/ntp_tiles/most_visited_sites_unittest.cc +++ b/components/ntp_tiles/most_visited_sites_unittest.cc
@@ -21,6 +21,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/task/cancelable_task_tracker.h" #include "base/test/sequenced_worker_pool_owner.h" +#include "base/threading/thread_task_runner_handle.h" #include "components/history/core/browser/top_sites.h" #include "components/history/core/browser/top_sites_observer.h" #include "components/ntp_tiles/icon_cacher.h"
diff --git a/components/policy/core/browser/proxy_policy_handler.cc b/components/policy/core/browser/proxy_policy_handler.cc index 5017e49..f74aa02 100644 --- a/components/policy/core/browser/proxy_policy_handler.cc +++ b/components/policy/core/browser/proxy_policy_handler.cc
@@ -56,8 +56,7 @@ // The proxy policies have the peculiarity that they are loaded from individual // policies, but the providers then expose them through a unified -// DictionaryValue. Once Dictionary policies are fully supported, the individual -// proxy policies will be deprecated. http://crbug.com/108996 +// DictionaryValue. ProxyPolicyHandler::ProxyPolicyHandler() {}
diff --git a/components/policy/core/common/policy_service_impl.cc b/components/policy/core/common/policy_service_impl.cc index 9947fee1..afdcffb0 100644 --- a/components/policy/core/common/policy_service_impl.cc +++ b/components/policy/core/common/policy_service_impl.cc
@@ -33,14 +33,11 @@ key::kProxyBypassList, }; -void FixDeprecatedPolicies(PolicyMap* policies) { - // Proxy settings have been configured by 5 policies that didn't mix well - // together, and maps of policies had to take this into account when merging - // policy sources. The proxy settings will eventually be configured by a - // single Dictionary policy when all providers have support for that. For - // now, the individual policies are mapped here to a single Dictionary policy - // that the rest of the policy machinery uses. - +// Maps the separate policies for proxy settings into a single Dictionary +// policy. This allows to keep the logic of merging policies from different +// sources simple, as all separate proxy policies should be considered as a +// single whole during merging. +void RemapProxyPolicies(PolicyMap* policies) { // The highest (level, scope) pair for an existing proxy policy is determined // first, and then only policies with those exact attributes are merged. PolicyMap::Entry current_priority; // Defaults to the lowest priority. @@ -194,7 +191,7 @@ for (auto provider : providers_) { PolicyBundle provided_bundle; provided_bundle.CopyFrom(provider->policies()); - FixDeprecatedPolicies(&provided_bundle.Get(chrome_namespace)); + RemapProxyPolicies(&provided_bundle.Get(chrome_namespace)); bundle.MergeFrom(provided_bundle); }
diff --git a/components/policy/core/common/policy_service_impl_unittest.cc b/components/policy/core/common/policy_service_impl_unittest.cc index da73e09..8324d19c 100644 --- a/components/policy/core/common/policy_service_impl_unittest.cc +++ b/components/policy/core/common/policy_service_impl_unittest.cc
@@ -662,7 +662,7 @@ policy_service_->RemoveObserver(POLICY_DOMAIN_SIGNIN_EXTENSIONS, &observer); } -TEST_F(PolicyServiceTest, FixDeprecatedPolicies) { +TEST_F(PolicyServiceTest, SeparateProxyPoliciesMerging) { const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string()); const PolicyNamespace extension_namespace(POLICY_DOMAIN_EXTENSIONS, "xyz");
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json index 4328073..e572e88 100644 --- a/components/policy/resources/policy_templates.json +++ b/components/policy/resources/policy_templates.json
@@ -139,7 +139,7 @@ # persistent IDs for all fields (but not for groups!) are needed. These are # specified by the 'id' keys of each policy. NEVER CHANGE EXISTING IDs, # because doing so would break the deployed wire format! -# For your editing convenience: highest ID currently used: 361 +# For your editing convenience: highest ID currently used: 362 # And don't forget to also update the EnterprisePolicies enum of # histograms.xml. # @@ -2002,10 +2002,6 @@ ], }, { - # TODO(joaodasilva): Make this the default and deprecate the other proxy - # policies once all providers are ready to load 'dict' policies. - # This is currently an internal policy. - # https://crbug.com/108992, https://crbug.com/108996 'name': 'ProxySettings', 'type': 'dict', 'schema': { @@ -9145,19 +9141,48 @@ The desire is to remove this override in the future.''', }, { - 'name': 'EnableMediaRouter', - 'type': 'main', - 'schema': { 'type': 'boolean' }, - 'supported_on': ['chrome.*:52-', 'chrome_os:52-', 'android:52-'], - 'features': { - 'dynamic_refresh': False, - 'per_profile': True, - }, - 'example_value': True, - 'id': 333, - 'caption': '''Enables cast''', - 'tags': [], - 'desc': '''If this is set to true or is not set, users will be able to cast tabs, sites or the desktop from the browser. If set to false, this option will be disabled.''' + 'name': 'GoogleCast', + 'type': 'group', + 'caption': '''Google Cast''', + 'desc': '''Configure policies for Google Cast, a feature that allows users to send the contents of tabs, sites or the desktop from the browser to remote displays and sound systems.''', + 'policies': [ + { + 'name': 'EnableMediaRouter', + 'type': 'main', + 'schema': { 'type': 'boolean' }, + 'supported_on': ['chrome.*:52-', 'chrome_os:52-', 'android:52-'], + 'features': { + 'dynamic_refresh': False, + 'per_profile': True, + }, + 'example_value': True, + 'id': 333, + 'caption': '''Enables Google Cast''', + 'tags': [], + 'desc': '''If this policy is set to true or is not set, Google Cast will be enabled, and users will be able to launch it from the app menu, page context menus, media controls on Cast-enabled websites, and (if shown) the Cast toolbar icon. + + If this policy set to false, Google Cast will be disabled.''' + }, + { + 'name': 'ShowCastIconInToolbar', + 'type': 'main', + 'schema': { 'type': 'boolean' }, + 'supported_on': ['chrome.*:58-', 'chrome_os:58-'], + 'features': { + 'dynamic_refresh': False, + 'per_profile': True, + }, + 'example_value': False, + 'id': 362, + 'caption': '''Shows the Google Cast toolbar icon''', + 'tags': [], + 'desc': '''If this policy is set to true, the Cast toolbar icon will always be shown on the toolbar or the overflow menu, and users will not be able to remove it. + + If this policy is set to false or is not set, users will be able to pin or remove the icon via its contextual menu. + + If the policy "EnableMediaRouter" is set to false, then this policy's value would have no effect, and the toolbar icon would not be shown.''' + }, + ], }, { 'name': 'ArcBackupRestoreEnabled',
diff --git a/components/spellcheck/renderer/spellcheck_provider_mac_unittest.cc b/components/spellcheck/renderer/spellcheck_provider_mac_unittest.cc index 1440313..a63411e 100644 --- a/components/spellcheck/renderer/spellcheck_provider_mac_unittest.cc +++ b/components/spellcheck/renderer/spellcheck_provider_mac_unittest.cc
@@ -11,7 +11,6 @@ #include "components/spellcheck/common/spellcheck_result.h" #include "components/spellcheck/renderer/spellcheck_provider_test.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/WebKit/public/platform/WebString.h" namespace { @@ -33,8 +32,7 @@ TEST_F(SpellCheckProviderMacTest, SingleRoundtripSuccess) { FakeTextCheckingCompletion completion; - provider_.RequestTextChecking(blink::WebString("hello "), - &completion, + provider_.RequestTextChecking(base::ASCIIToUTF16("hello "), &completion, std::vector<SpellCheckMarker>()); EXPECT_EQ(completion.completion_count_, 0U); EXPECT_EQ(provider_.messages_.size(), 1U); @@ -44,7 +42,7 @@ bool ok = SpellCheckHostMsg_RequestTextCheck::Read( provider_.messages_[0], &read_parameters1); EXPECT_TRUE(ok); - EXPECT_EQ(std::get<2>(read_parameters1), base::UTF8ToUTF16("hello ")); + EXPECT_EQ(std::get<2>(read_parameters1), base::ASCIIToUTF16("hello ")); FakeMessageArrival(&provider_, read_parameters1); EXPECT_EQ(completion.completion_count_, 1U); @@ -53,12 +51,10 @@ TEST_F(SpellCheckProviderMacTest, TwoRoundtripSuccess) { FakeTextCheckingCompletion completion1; - provider_.RequestTextChecking(blink::WebString("hello "), - &completion1, + provider_.RequestTextChecking(base::ASCIIToUTF16("hello "), &completion1, std::vector<SpellCheckMarker>()); FakeTextCheckingCompletion completion2; - provider_.RequestTextChecking(blink::WebString("bye "), - &completion2, + provider_.RequestTextChecking(base::ASCIIToUTF16("bye "), &completion2, std::vector<SpellCheckMarker>()); EXPECT_EQ(completion1.completion_count_, 0U);
diff --git a/components/ukm/ukm_service.cc b/components/ukm/ukm_service.cc index 42bb7ad3..e050b4bb 100644 --- a/components/ukm/ukm_service.cc +++ b/components/ukm/ukm_service.cc
@@ -14,6 +14,7 @@ #include "base/metrics/histogram_macros.h" #include "base/rand_util.h" #include "base/strings/string_number_conversions.h" +#include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "components/metrics/metrics_log.h" #include "components/metrics/metrics_log_uploader.h"
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index e407460c..325ea9e 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -812,6 +812,8 @@ "loader/navigation_url_loader_impl_core.h", "loader/netlog_observer.cc", "loader/netlog_observer.h", + "loader/null_resource_controller.cc", + "loader/null_resource_controller.h", "loader/power_save_block_resource_throttle.cc", "loader/power_save_block_resource_throttle.h", "loader/redirect_to_file_resource_handler.cc",
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc index a30bab3..4c5e325 100644 --- a/content/browser/browser_main_loop.cc +++ b/content/browser/browser_main_loop.cc
@@ -1470,12 +1470,11 @@ ImageTransportFactory::Initialize(); ImageTransportFactory::GetInstance()->SetGpuChannelEstablishFactory(factory); #if defined(USE_AURA) - bool use_mus_in_renderer = base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kUseMusInRenderer); - if (aura::Env::GetInstance() && !use_mus_in_renderer) { - aura::Env::GetInstance()->set_context_factory(GetContextFactory()); - aura::Env::GetInstance()->set_context_factory_private( - GetContextFactoryPrivate()); + bool use_mus_in_renderer = !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kNoUseMusInRenderer); + if (!use_mus_in_renderer || env_->mode() == aura::Env::Mode::LOCAL) { + env_->set_context_factory(GetContextFactory()); + env_->set_context_factory_private(GetContextFactoryPrivate()); } #endif // defined(USE_AURA) #endif // defined(OS_ANDROID)
diff --git a/content/browser/download/download_resource_handler.cc b/content/browser/download/download_resource_handler.cc index 30bcd5e..30cf6fa 100644 --- a/content/browser/download/download_resource_handler.cc +++ b/content/browser/download/download_resource_handler.cc
@@ -139,24 +139,34 @@ return handler; } -bool DownloadResourceHandler::OnRequestRedirected( +void DownloadResourceHandler::OnRequestRedirected( const net::RedirectInfo& redirect_info, ResourceResponse* response, - bool* defer) { - return core_.OnRequestRedirected(); + std::unique_ptr<ResourceController> controller) { + if (core_.OnRequestRedirected()) { + controller->Resume(); + } else { + controller->Cancel(); + } } // Send the download creation information to the download thread. -bool DownloadResourceHandler::OnResponseStarted( +void DownloadResourceHandler::OnResponseStarted( ResourceResponse* response, - bool* defer) { + std::unique_ptr<ResourceController> controller) { // The MIME type in ResourceResponse is the product of // MimeTypeResourceHandler. - return core_.OnResponseStarted(response->head.mime_type); + if (core_.OnResponseStarted(response->head.mime_type)) { + controller->Resume(); + } else { + controller->Cancel(); + } } -bool DownloadResourceHandler::OnWillStart(const GURL& url, bool* defer) { - return true; +void DownloadResourceHandler::OnWillStart( + const GURL& url, + std::unique_ptr<ResourceController> controller) { + controller->Resume(); } // Create a new buffer, which will be handed to the download thread for file @@ -168,14 +178,29 @@ } // Pass the buffer to the download file writer. -bool DownloadResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { - return core_.OnReadCompleted(bytes_read, defer); +void DownloadResourceHandler::OnReadCompleted( + int bytes_read, + std::unique_ptr<ResourceController> controller) { + DCHECK(!has_controller()); + + bool defer = false; + if (!core_.OnReadCompleted(bytes_read, &defer)) { + controller->Cancel(); + return; + } + + if (defer) { + HoldController(std::move(controller)); + } else { + controller->Resume(); + } } void DownloadResourceHandler::OnResponseCompleted( const net::URLRequestStatus& status, - bool* defer) { + std::unique_ptr<ResourceController> controller) { core_.OnResponseCompleted(status); + controller->Resume(); } void DownloadResourceHandler::OnDataDownloaded(int bytes_downloaded) { @@ -226,7 +251,7 @@ void DownloadResourceHandler::OnReadyToRead() { DCHECK_CURRENTLY_ON(BrowserThread::IO); - controller()->Resume(); + Resume(); } void DownloadResourceHandler::CancelRequest() {
diff --git a/content/browser/download/download_resource_handler.h b/content/browser/download/download_resource_handler.h index b2820a39..360770a 100644 --- a/content/browser/download/download_resource_handler.h +++ b/content/browser/download/download_resource_handler.h
@@ -26,6 +26,7 @@ namespace content { class ByteStreamReader; struct DownloadCreateInfo; +class ResourceController; // Forwards data to the download thread. class CONTENT_EXPORT DownloadResourceHandler @@ -49,15 +50,19 @@ // ResourceDispatcherHostImpl. static std::unique_ptr<ResourceHandler> Create(net::URLRequest* request); - bool OnRequestRedirected(const net::RedirectInfo& redirect_info, - ResourceResponse* response, - bool* defer) override; + void OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; // Send the download creation information to the download thread. - bool OnResponseStarted(ResourceResponse* response, bool* defer) override; + void OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; // Pass-through implementation. - bool OnWillStart(const GURL& url, bool* defer) override; + void OnWillStart(const GURL& url, + std::unique_ptr<ResourceController> controller) override; // Create a new buffer, which will be handed to the download thread for file // writing and deletion. @@ -65,10 +70,12 @@ int* buf_size, int min_size) override; - bool OnReadCompleted(int bytes_read, bool* defer) override; + void OnReadCompleted(int bytes_read, + std::unique_ptr<ResourceController> controller) override; - void OnResponseCompleted(const net::URLRequestStatus& status, - bool* defer) override; + void OnResponseCompleted( + const net::URLRequestStatus& status, + std::unique_ptr<ResourceController> controller) override; // N/A to this flavor of DownloadHandler. void OnDataDownloaded(int bytes_downloaded) override; @@ -100,6 +107,7 @@ std::unique_ptr<DownloadTabInfo> tab_info_; DownloadRequestCore core_; + DISALLOW_COPY_AND_ASSIGN(DownloadResourceHandler); };
diff --git a/content/browser/download/save_file_resource_handler.cc b/content/browser/download/save_file_resource_handler.cc index 854c15a..6ac4a09e 100644 --- a/content/browser/download/save_file_resource_handler.cc +++ b/content/browser/download/save_file_resource_handler.cc
@@ -9,6 +9,7 @@ #include "base/message_loop/message_loop.h" #include "base/strings/string_number_conversions.h" #include "content/browser/download/save_file_manager.h" +#include "content/browser/loader/resource_controller.h" #include "content/public/browser/browser_thread.h" #include "net/base/io_buffer.h" #include "net/url_request/redirect_info.h" @@ -37,16 +38,17 @@ SaveFileResourceHandler::~SaveFileResourceHandler() { } -bool SaveFileResourceHandler::OnRequestRedirected( +void SaveFileResourceHandler::OnRequestRedirected( const net::RedirectInfo& redirect_info, ResourceResponse* response, - bool* defer) { + std::unique_ptr<ResourceController> controller) { final_url_ = redirect_info.new_url; - return true; + controller->Resume(); } -bool SaveFileResourceHandler::OnResponseStarted(ResourceResponse* response, - bool* defer) { +void SaveFileResourceHandler::OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) { // |save_manager_| consumes (deletes): SaveFileCreateInfo* info = new SaveFileCreateInfo( url_, final_url_, save_item_id_, save_package_id_, render_process_id_, @@ -55,11 +57,17 @@ BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, base::Bind(&SaveFileManager::StartSave, save_manager_, info)); - return true; + controller->Resume(); } -bool SaveFileResourceHandler::OnWillStart(const GURL& url, bool* defer) { - return authorization_state_ == AuthorizationState::AUTHORIZED; +void SaveFileResourceHandler::OnWillStart( + const GURL& url, + std::unique_ptr<ResourceController> controller) { + if (authorization_state_ == AuthorizationState::AUTHORIZED) { + controller->Resume(); + } else { + controller->Cancel(); + } } bool SaveFileResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, @@ -75,7 +83,9 @@ return true; } -bool SaveFileResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { +void SaveFileResourceHandler::OnReadCompleted( + int bytes_read, + std::unique_ptr<ResourceController> controller) { DCHECK_EQ(AuthorizationState::AUTHORIZED, authorization_state_); DCHECK(read_buffer_.get()); // We are passing ownership of this buffer to the save file manager. @@ -85,12 +95,12 @@ BrowserThread::FILE, FROM_HERE, base::Bind(&SaveFileManager::UpdateSaveProgress, save_manager_, save_item_id_, base::RetainedRef(buffer), bytes_read)); - return true; + controller->Resume(); } void SaveFileResourceHandler::OnResponseCompleted( const net::URLRequestStatus& status, - bool* defer) { + std::unique_ptr<ResourceController> controller) { if (authorization_state_ != AuthorizationState::AUTHORIZED) DCHECK(!status.is_success()); @@ -99,7 +109,8 @@ base::Bind(&SaveFileManager::SaveFinished, save_manager_, save_item_id_, save_package_id_, status.is_success() && !status.is_io_pending())); - read_buffer_ = NULL; + read_buffer_ = nullptr; + controller->Resume(); } void SaveFileResourceHandler::OnDataDownloaded(int bytes_downloaded) {
diff --git a/content/browser/download/save_file_resource_handler.h b/content/browser/download/save_file_resource_handler.h index 950e49a..ef8f97e4 100644 --- a/content/browser/download/save_file_resource_handler.h +++ b/content/browser/download/save_file_resource_handler.h
@@ -20,6 +20,7 @@ } namespace content { +class ResourceController; class SaveFileManager; // Forwards data to the save thread. @@ -50,15 +51,19 @@ // Saves the redirected URL to final_url_, we need to use the original // URL to match original request. - bool OnRequestRedirected(const net::RedirectInfo& redirect_info, - ResourceResponse* response, - bool* defer) override; + void OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; // Sends the download creation information to the download thread. - bool OnResponseStarted(ResourceResponse* response, bool* defer) override; + void OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; // Pass-through implementation. - bool OnWillStart(const GURL& url, bool* defer) override; + void OnWillStart(const GURL& url, + std::unique_ptr<ResourceController> controller) override; // Creates a new buffer, which will be handed to the download thread for file // writing and deletion. @@ -67,10 +72,12 @@ int min_size) override; // Passes the buffer to the download file writer. - bool OnReadCompleted(int bytes_read, bool* defer) override; + void OnReadCompleted(int bytes_read, + std::unique_ptr<ResourceController> controller) override; - void OnResponseCompleted(const net::URLRequestStatus& status, - bool* defer) override; + void OnResponseCompleted( + const net::URLRequestStatus& status, + std::unique_ptr<ResourceController> controller) override; // N/A to this flavor of SaveFileResourceHandler. void OnDataDownloaded(int bytes_downloaded) override;
diff --git a/content/browser/loader/DEPS b/content/browser/loader/DEPS index 1f22101..9a132f78 100644 --- a/content/browser/loader/DEPS +++ b/content/browser/loader/DEPS
@@ -81,6 +81,7 @@ "layered_resource_handler\.(cc|h)": [ "-content", "+content/browser/loader/layered_resource_handler.h", + "+content/browser/loader/resource_controller.h", "+content/browser/loader/resource_handler.h", "+content/common/content_export.h", ], @@ -150,6 +151,7 @@ "+content/browser/loader/global_routing_id.h", "+content/browser/loader/loader_delegate.h", "+content/browser/loader/mojo_async_resource_handler.h", + "+content/browser/loader/null_resource_controller.h", "+content/browser/loader/power_save_block_resource_throttle.h", "+content/browser/loader/resource_dispatcher_host_impl.h", "+content/browser/loader/resource_loader.h", @@ -226,6 +228,7 @@ ], "resource_handler\.(cc|h)": [ "-content", + "+content/browser/loader/resource_controller.h", "+content/browser/loader/resource_handler.h", "+content/browser/loader/resource_request_info_impl.h", "+content/common/content_export.h", @@ -288,6 +291,7 @@ ], "resource_requester_info\.(cc|h)": [ "-content", + "+content/browser/loader/resource_controller.h", "+content/browser/loader/resource_requester_info.h", "+content/common/content_export.h", "+content/public/browser/resource_context.h", @@ -302,9 +306,9 @@ ], "resource_scheduler\.(cc|h)": [ "-content", + "+content/browser/loader/resource_controller.h", "+content/browser/loader/resource_scheduler.h", "+content/common/content_export.h", - "+content/public/browser/resource_controller.h", "+content/public/browser/resource_request_info.h", "+content/public/browser/resource_throttle.h", @@ -325,6 +329,7 @@ "sync_resource_handler\.(cc|h)": [ "-content", "+content/browser/loader/netlog_observer.h", + "+content/browser/loader/resource_controller.h", "+content/browser/loader/resource_dispatcher_host_impl.h", "+content/browser/loader/resource_handler.h", "+content/browser/loader/resource_request_info_impl.h",
diff --git a/content/browser/loader/async_resource_handler.cc b/content/browser/loader/async_resource_handler.cc index 3481fc5..05d05de 100644 --- a/content/browser/loader/async_resource_handler.cc +++ b/content/browser/loader/async_resource_handler.cc
@@ -211,7 +211,6 @@ pending_data_count_(0), allocation_size_(0), total_read_body_bytes_(0), - did_defer_(false), has_checked_for_sufficient_resources_(false), sent_received_response_msg_(false), sent_data_buffer_msg_(false), @@ -261,16 +260,15 @@ upload_progress_tracker_->OnAckReceived(); } -bool AsyncResourceHandler::OnRequestRedirected( +void AsyncResourceHandler::OnRequestRedirected( const net::RedirectInfo& redirect_info, ResourceResponse* response, - bool* defer) { + std::unique_ptr<ResourceController> controller) { ResourceMessageFilter* filter = GetFilter(); - if (!filter) - return false; - - *defer = did_defer_ = true; - OnDefer(); + if (!filter) { + controller->Cancel(); + return; + } NetLogObserver::PopulateResponseInfo(request(), response); response->head.encoded_data_length = request()->GetTotalReceivedBytes(); @@ -281,17 +279,23 @@ // cookies? The only case where it can change is top-level navigation requests // and hopefully those will eventually all be owned by the browser. It's // possible this is still needed while renderer-owned ones exist. - return filter->Send(new ResourceMsg_ReceivedRedirect( - GetRequestID(), redirect_info, response->head)); + if (filter->Send(new ResourceMsg_ReceivedRedirect( + GetRequestID(), redirect_info, response->head))) { + OnDefer(std::move(controller)); + } else { + controller->Cancel(); + } } -bool AsyncResourceHandler::OnResponseStarted(ResourceResponse* response, - bool* defer) { +void AsyncResourceHandler::OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) { // For changes to the main frame, inform the renderer of the new URL's // per-host settings before the request actually commits. This way the // renderer will be able to set these precisely at the time the // request commits, avoiding the possibility of e.g. zooming the old content // or of having to layout the new content twice. + DCHECK(!has_controller()); response_started_ticks_ = base::TimeTicks::Now(); @@ -310,8 +314,10 @@ } ResourceMessageFilter* filter = GetFilter(); - if (!filter) - return false; + if (!filter) { + controller->Cancel(); + return; + } NetLogObserver::PopulateResponseInfo(request(), response); response->head.encoded_data_length = request()->raw_header_size(); @@ -338,13 +344,17 @@ } inlining_helper_->OnResponseReceived(*response); - return true; + controller->Resume(); } -bool AsyncResourceHandler::OnWillStart(const GURL& url, bool* defer) { +void AsyncResourceHandler::OnWillStart( + const GURL& url, + std::unique_ptr<ResourceController> controller) { ResourceMessageFilter* filter = GetFilter(); - if (!filter) - return false; + if (!filter) { + controller->Cancel(); + return; + } if (GetRequestInfo()->is_upload_progress_enabled() && request()->has_upload()) { @@ -354,14 +364,16 @@ base::Unretained(this)), request()); } - return true; + controller->Resume(); } bool AsyncResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, int* buf_size, int min_size) { + DCHECK(!has_controller()); DCHECK_EQ(-1, min_size); + // TODO(mmenke): Should fail with ERR_INSUFFICIENT_RESOURCES here. if (!CheckForSufficientResource()) return false; @@ -383,15 +395,22 @@ return true; } -bool AsyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { +void AsyncResourceHandler::OnReadCompleted( + int bytes_read, + std::unique_ptr<ResourceController> controller) { + DCHECK(!has_controller()); DCHECK_GE(bytes_read, 0); - if (!bytes_read) - return true; + if (!bytes_read) { + controller->Resume(); + return; + } ResourceMessageFilter* filter = GetFilter(); - if (!filter) - return false; + if (!filter) { + controller->Cancel(); + return; + } int encoded_data_length = CalculateEncodedDataLengthToReport(); if (!first_chunk_read_) @@ -401,9 +420,10 @@ // Return early if InliningHelper handled the received data. if (inlining_helper_->SendInlinedDataIfApplicable( - bytes_read, encoded_data_length, filter, - GetRequestID())) - return true; + bytes_read, encoded_data_length, filter, GetRequestID())) { + controller->Resume(); + return; + } buffer_->ShrinkLastAllocation(bytes_read); @@ -412,8 +432,10 @@ if (!sent_data_buffer_msg_) { base::SharedMemoryHandle handle = base::SharedMemory::DuplicateHandle( buffer_->GetSharedMemory().handle()); - if (!base::SharedMemory::IsHandleValid(handle)) - return false; + if (!base::SharedMemory::IsHandleValid(handle)) { + controller->Cancel(); + return; + } filter->Send(new ResourceMsg_SetDataBuffer( GetRequestID(), handle, buffer_->GetSharedMemory().mapped_size(), filter->peer_pid())); @@ -427,11 +449,10 @@ ++pending_data_count_; if (!buffer_->CanAllocate()) { - *defer = did_defer_ = true; - OnDefer(); + OnDefer(std::move(controller)); + } else { + controller->Resume(); } - - return true; } void AsyncResourceHandler::OnDataDownloaded(int bytes_downloaded) { @@ -446,10 +467,12 @@ void AsyncResourceHandler::OnResponseCompleted( const net::URLRequestStatus& status, - bool* defer) { + std::unique_ptr<ResourceController> controller) { ResourceMessageFilter* filter = GetFilter(); - if (!filter) + if (!filter) { + controller->Resume(); return; + } // Ensure sending the final upload progress message here, since // OnResponseCompleted can be called without OnResponseStarted on cancellation @@ -496,6 +519,7 @@ if (status.is_success()) RecordHistogram(); + controller->Resume(); } bool AsyncResourceHandler::EnsureResourceBufferIsInitialized() { @@ -511,14 +535,15 @@ } void AsyncResourceHandler::ResumeIfDeferred() { - if (did_defer_) { - did_defer_ = false; + if (has_controller()) { request()->LogUnblocked(); - controller()->Resume(); + Resume(); } } -void AsyncResourceHandler::OnDefer() { +void AsyncResourceHandler::OnDefer( + std::unique_ptr<ResourceController> controller) { + HoldController(std::move(controller)); request()->LogBlockedBy("AsyncResourceHandler"); } @@ -530,7 +555,6 @@ if (rdh_->HasSufficientResourcesForRequest(request())) return true; - controller()->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); return false; }
diff --git a/content/browser/loader/async_resource_handler.h b/content/browser/loader/async_resource_handler.h index a664c3e..f498d4c 100644 --- a/content/browser/loader/async_resource_handler.h +++ b/content/browser/loader/async_resource_handler.h
@@ -25,6 +25,7 @@ namespace content { class ResourceBuffer; +class ResourceController; class ResourceDispatcherHostImpl; class UploadProgressTracker; @@ -40,17 +41,23 @@ bool OnMessageReceived(const IPC::Message& message) override; // ResourceHandler implementation: - bool OnRequestRedirected(const net::RedirectInfo& redirect_info, - ResourceResponse* response, - bool* defer) override; - bool OnResponseStarted(ResourceResponse* response, bool* defer) override; - bool OnWillStart(const GURL& url, bool* defer) override; + void OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; + void OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; + void OnWillStart(const GURL& url, + std::unique_ptr<ResourceController> controller) override; bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, int* buf_size, int min_size) override; - bool OnReadCompleted(int bytes_read, bool* defer) override; - void OnResponseCompleted(const net::URLRequestStatus& status, - bool* defer) override; + void OnReadCompleted(int bytes_read, + std::unique_ptr<ResourceController> controller) override; + void OnResponseCompleted( + const net::URLRequestStatus& status, + std::unique_ptr<ResourceController> controller) override; void OnDataDownloaded(int bytes_downloaded) override; private: @@ -63,7 +70,7 @@ bool EnsureResourceBufferIsInitialized(); void ResumeIfDeferred(); - void OnDefer(); + void OnDefer(std::unique_ptr<ResourceController> controller); bool CheckForSufficientResource(); int CalculateEncodedDataLengthToReport(); int CalculateEncodedBodyLengthToReport(); @@ -85,8 +92,6 @@ bool first_chunk_read_ = false; - bool did_defer_; - bool has_checked_for_sufficient_resources_; bool sent_received_response_msg_; bool sent_data_buffer_msg_;
diff --git a/content/browser/loader/detachable_resource_handler.cc b/content/browser/loader/detachable_resource_handler.cc index 2e1af5e..85ac2d3 100644 --- a/content/browser/loader/detachable_resource_handler.cc +++ b/content/browser/loader/detachable_resource_handler.cc
@@ -7,7 +7,10 @@ #include <utility> #include "base/logging.h" +#include "base/memory/ptr_util.h" #include "base/time/time.h" +#include "content/browser/loader/null_resource_controller.h" +#include "content/browser/loader/resource_controller.h" #include "content/browser/loader/resource_request_info_impl.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" @@ -21,6 +24,53 @@ namespace content { +// ResourceController that, when invoked, runs the corresponding method on +// ResourceHandler. +class DetachableResourceHandler::Controller : public ResourceController { + public: + explicit Controller(DetachableResourceHandler* detachable_handler) + : detachable_handler_(detachable_handler){}; + + ~Controller() override {} + + // ResourceController implementation: + void Resume() override { + MarkAsUsed(); + detachable_handler_->Resume(); + } + + void Cancel() override { + MarkAsUsed(); + detachable_handler_->Cancel(); + } + + void CancelAndIgnore() override { + MarkAsUsed(); + detachable_handler_->CancelAndIgnore(); + } + + void CancelWithError(int error_code) override { + MarkAsUsed(); + detachable_handler_->CancelWithError(error_code); + } + + private: + void MarkAsUsed() { +#if DCHECK_IS_ON() + DCHECK(!used_); + used_ = true; +#endif + } + +#if DCHECK_IS_ON() + bool used_ = false; +#endif + + DetachableResourceHandler* detachable_handler_; + + DISALLOW_COPY_AND_ASSIGN(Controller); +}; + DetachableResourceHandler::DetachableResourceHandler( net::URLRequest* request, base::TimeDelta cancel_delay, @@ -28,7 +78,6 @@ : ResourceHandler(request), next_handler_(std::move(next_handler)), cancel_delay_(cancel_delay), - is_deferred_(false), is_finished_(false) { GetRequestInfo()->set_detachable_handler(this); } @@ -38,6 +87,12 @@ GetRequestInfo()->set_detachable_handler(NULL); } +void DetachableResourceHandler::SetDelegate(Delegate* delegate) { + ResourceHandler::SetDelegate(delegate); + if (next_handler_) + next_handler_->SetDelegate(delegate); +} + void DetachableResourceHandler::Detach() { if (is_detached()) return; @@ -46,9 +101,12 @@ // Simulate a cancel on the next handler before destroying it. net::URLRequestStatus status(net::URLRequestStatus::CANCELED, net::ERR_ABORTED); - bool defer_ignored = false; - next_handler_->OnResponseCompleted(status, &defer_ignored); - DCHECK(!defer_ignored); + bool was_resumed; + // TODO(mmenke): Get rid of NullResourceController and do something more + // reasonable. + next_handler_->OnResponseCompleted( + status, base::MakeUnique<NullResourceController>(&was_resumed)); + DCHECK(was_resumed); // If |next_handler_| were to defer its shutdown in OnResponseCompleted, // this would destroy it anyway. Fortunately, AsyncResourceHandler never // does this anyway, so DCHECK it. MimeTypeResourceHandler and RVH shutdown @@ -66,13 +124,13 @@ // Time the request out if it takes too long. detached_timer_.reset(new base::OneShotTimer()); - detached_timer_->Start( - FROM_HERE, cancel_delay_, this, &DetachableResourceHandler::Cancel); + detached_timer_->Start(FROM_HERE, cancel_delay_, this, + &DetachableResourceHandler::OnTimedOut); // Resume if necessary. The request may have been deferred, say, waiting on a // full buffer in AsyncResourceHandler. Now that it has been detached, resume // and drain it. - if (is_deferred_) { + if (has_controller()) { // The nested ResourceHandler may have logged that it's blocking the // request. Log it as no longer doing so, to avoid a DCHECK on resume. request()->LogUnblocked(); @@ -80,52 +138,49 @@ } } -void DetachableResourceHandler::SetController(ResourceController* controller) { - ResourceHandler::SetController(controller); - - // Intercept the ResourceController for downstream handlers to keep track of - // whether the request is deferred. - if (next_handler_) - next_handler_->SetController(this); -} - -bool DetachableResourceHandler::OnRequestRedirected( +void DetachableResourceHandler::OnRequestRedirected( const net::RedirectInfo& redirect_info, ResourceResponse* response, - bool* defer) { - DCHECK(!is_deferred_); + std::unique_ptr<ResourceController> controller) { + DCHECK(!has_controller()); - if (!next_handler_) - return true; + if (!next_handler_) { + controller->Resume(); + return; + } - bool ret = next_handler_->OnRequestRedirected( - redirect_info, response, &is_deferred_); - *defer = is_deferred_; - return ret; + HoldController(std::move(controller)); + next_handler_->OnRequestRedirected(redirect_info, response, + base::MakeUnique<Controller>(this)); } -bool DetachableResourceHandler::OnResponseStarted(ResourceResponse* response, - bool* defer) { - DCHECK(!is_deferred_); +void DetachableResourceHandler::OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) { + DCHECK(!has_controller()); - if (!next_handler_) - return true; + if (!next_handler_) { + controller->Resume(); + return; + } - bool ret = - next_handler_->OnResponseStarted(response, &is_deferred_); - *defer = is_deferred_; - return ret; + HoldController(std::move(controller)); + next_handler_->OnResponseStarted(response, + base::MakeUnique<Controller>(this)); } -bool DetachableResourceHandler::OnWillStart(const GURL& url, bool* defer) { - DCHECK(!is_deferred_); +void DetachableResourceHandler::OnWillStart( + const GURL& url, + std::unique_ptr<ResourceController> controller) { + DCHECK(!has_controller()); - if (!next_handler_) - return true; + if (!next_handler_) { + controller->Resume(); + return; + } - bool ret = next_handler_->OnWillStart(url, &is_deferred_); - *defer = is_deferred_; - return ret; + HoldController(std::move(controller)); + next_handler_->OnWillStart(url, base::MakeUnique<Controller>(this)); } bool DetachableResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, @@ -143,31 +198,35 @@ return next_handler_->OnWillRead(buf, buf_size, min_size); } -bool DetachableResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { - DCHECK(!is_deferred_); +void DetachableResourceHandler::OnReadCompleted( + int bytes_read, + std::unique_ptr<ResourceController> controller) { + DCHECK(!has_controller()); - if (!next_handler_) - return true; + if (!next_handler_) { + controller->Resume(); + return; + } - bool ret = - next_handler_->OnReadCompleted(bytes_read, &is_deferred_); - *defer = is_deferred_; - return ret; + HoldController(std::move(controller)); + next_handler_->OnReadCompleted(bytes_read, + base::MakeUnique<Controller>(this)); } void DetachableResourceHandler::OnResponseCompleted( const net::URLRequestStatus& status, - bool* defer) { + std::unique_ptr<ResourceController> controller) { // No DCHECK(!is_deferred_) as the request may have been cancelled while // deferred. - if (!next_handler_) + if (!next_handler_) { + controller->Resume(); return; + } is_finished_ = true; - next_handler_->OnResponseCompleted(status, &is_deferred_); - *defer = is_deferred_; + next_handler_->OnResponseCompleted(status, std::move(controller)); } void DetachableResourceHandler::OnDataDownloaded(int bytes_downloaded) { @@ -177,22 +236,13 @@ next_handler_->OnDataDownloaded(bytes_downloaded); } -void DetachableResourceHandler::Resume() { - DCHECK(is_deferred_); - is_deferred_ = false; - controller()->Resume(); -} +void DetachableResourceHandler::OnTimedOut() { + // Requests are only timed out after being detached, and shouldn't be deferred + // once detached. + DCHECK(!next_handler_); + DCHECK(!has_controller()); -void DetachableResourceHandler::Cancel() { - controller()->Cancel(); -} - -void DetachableResourceHandler::CancelAndIgnore() { - controller()->CancelAndIgnore(); -} - -void DetachableResourceHandler::CancelWithError(int error_code) { - controller()->CancelWithError(error_code); + OutOfBandCancel(net::ERR_ABORTED, true /* tell_renderer */); } } // namespace content
diff --git a/content/browser/loader/detachable_resource_handler.h b/content/browser/loader/detachable_resource_handler.h index f75506e..cda6bbc 100644 --- a/content/browser/loader/detachable_resource_handler.h +++ b/content/browser/loader/detachable_resource_handler.h
@@ -22,6 +22,8 @@ namespace content { +class ResourceController; + // A ResourceHandler which delegates all calls to the next handler, unless // detached. Once detached, it drives the request to completion itself. This is // used for requests which outlive the owning renderer, such as <link @@ -32,14 +34,15 @@ // // Note that, once detached, the request continues without the original next // handler, so any policy decisions in that handler are skipped. -class DetachableResourceHandler : public ResourceHandler, - public ResourceController { +class DetachableResourceHandler : public ResourceHandler { public: DetachableResourceHandler(net::URLRequest* request, base::TimeDelta cancel_delay, std::unique_ptr<ResourceHandler> next_handler); ~DetachableResourceHandler() override; + void SetDelegate(Delegate* delegate) override; + bool is_detached() const { return next_handler_ == NULL; } void Detach(); @@ -48,34 +51,36 @@ } // ResourceHandler implementation: - void SetController(ResourceController* controller) override; - bool OnRequestRedirected(const net::RedirectInfo& redirect_info, - ResourceResponse* response, - bool* defer) override; - bool OnResponseStarted(ResourceResponse* response, bool* defer) override; - bool OnWillStart(const GURL& url, bool* defer) override; + void OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; + void OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; + void OnWillStart(const GURL& url, + std::unique_ptr<ResourceController> controller) override; bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, int* buf_size, int min_size) override; - bool OnReadCompleted(int bytes_read, bool* defer) override; - void OnResponseCompleted(const net::URLRequestStatus& status, - bool* defer) override; + void OnReadCompleted(int bytes_read, + std::unique_ptr<ResourceController> controller) override; + void OnResponseCompleted( + const net::URLRequestStatus& status, + std::unique_ptr<ResourceController> controller) override; void OnDataDownloaded(int bytes_downloaded) override; - // ResourceController implementation: - void Resume() override; - void Cancel() override; - void CancelAndIgnore() override; - void CancelWithError(int error_code) override; - private: + class Controller; + + void OnTimedOut(); + std::unique_ptr<ResourceHandler> next_handler_; scoped_refptr<net::IOBuffer> read_buffer_; std::unique_ptr<base::OneShotTimer> detached_timer_; base::TimeDelta cancel_delay_; - bool is_deferred_; bool is_finished_; DISALLOW_COPY_AND_ASSIGN(DetachableResourceHandler);
diff --git a/content/browser/loader/intercepting_resource_handler.cc b/content/browser/loader/intercepting_resource_handler.cc index 733ca71d2..f1527f0 100644 --- a/content/browser/loader/intercepting_resource_handler.cc +++ b/content/browser/loader/intercepting_resource_handler.cc
@@ -4,59 +4,92 @@ #include "content/browser/loader/intercepting_resource_handler.h" +#include "base/auto_reset.h" #include "base/location.h" #include "base/logging.h" +#include "base/memory/ptr_util.h" #include "base/strings/string_util.h" #include "base/threading/thread_task_runner_handle.h" +#include "content/browser/loader/null_resource_controller.h" +#include "content/browser/loader/resource_controller.h" #include "content/public/common/resource_response.h" #include "net/base/io_buffer.h" #include "net/url_request/url_request.h" namespace content { +class InterceptingResourceHandler::Controller : public ResourceController { + public: + explicit Controller(InterceptingResourceHandler* mime_handler) + : intercepting_handler_(mime_handler) {} + + void Resume() override { + MarkAsUsed(); + intercepting_handler_->ResumeInternal(); + } + + void Cancel() override { + MarkAsUsed(); + intercepting_handler_->Cancel(); + } + + void CancelAndIgnore() override { + MarkAsUsed(); + intercepting_handler_->CancelAndIgnore(); + } + + void CancelWithError(int error_code) override { + MarkAsUsed(); + intercepting_handler_->CancelWithError(error_code); + } + + private: + void MarkAsUsed() { +#if DCHECK_IS_ON() + DCHECK(!used_); + used_ = true; +#endif + } + +#if DCHECK_IS_ON() + bool used_ = false; +#endif + InterceptingResourceHandler* intercepting_handler_; + + DISALLOW_COPY_AND_ASSIGN(Controller); +}; + InterceptingResourceHandler::InterceptingResourceHandler( std::unique_ptr<ResourceHandler> next_handler, net::URLRequest* request) : LayeredResourceHandler(request, std::move(next_handler)), weak_ptr_factory_(this) { - next_handler_->SetController(this); } InterceptingResourceHandler::~InterceptingResourceHandler() {} -void InterceptingResourceHandler::SetController( - ResourceController* controller) { - if (state_ == State::PASS_THROUGH) - return LayeredResourceHandler::SetController(controller); - ResourceHandler::SetController(controller); -} - -bool InterceptingResourceHandler::OnResponseStarted(ResourceResponse* response, - bool* defer) { +void InterceptingResourceHandler::OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) { // If there's no need to switch handlers, just start acting as a blind // pass-through ResourceHandler. if (!new_handler_) { state_ = State::PASS_THROUGH; - next_handler_->SetController(controller()); - return next_handler_->OnResponseStarted(response, defer); + next_handler_->OnResponseStarted(response, std::move(controller)); + return; } DCHECK_EQ(state_, State::STARTING); - // Otherwise, switch handlers. First, inform the original ResourceHandler - // that this will be handled entirely by the new ResourceHandler. - bool defer_ignored = false; - if (!next_handler_->OnResponseStarted(response, &defer_ignored)) - return false; - - // Although deferring OnResponseStarted is legal, the only downstream handler - // which does so is CrossSiteResourceHandler. Cross-site transitions should - // not trigger when switching handlers. - DCHECK(!defer_ignored); // TODO(yhirano): Retaining ownership from a raw pointer is bad. response_ = response; - state_ = State::SENDING_PAYLOAD_TO_OLD_HANDLER; - return DoLoop(defer); + + // Otherwise, switch handlers. First, inform the original ResourceHandler + // that this will be handled entirely by the new ResourceHandler. + HoldController(std::move(controller)); + state_ = State::SWAPPING_HANDLERS; + + DoLoop(); } bool InterceptingResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, @@ -78,7 +111,11 @@ return true; } -bool InterceptingResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { +void InterceptingResourceHandler::OnReadCompleted( + int bytes_read, + std::unique_ptr<ResourceController> controller) { + DCHECK(!has_controller()); + DCHECK_GE(bytes_read, 0); if (state_ == State::PASS_THROUGH) { if (first_read_buffer_double_) { @@ -89,88 +126,67 @@ first_read_buffer_ = nullptr; first_read_buffer_double_ = nullptr; } - return next_handler_->OnReadCompleted(bytes_read, defer); + next_handler_->OnReadCompleted(bytes_read, std::move(controller)); + return; } DCHECK_EQ(State::WAITING_FOR_ON_READ_COMPLETED, state_); first_read_buffer_bytes_read_ = bytes_read; state_ = State::SENDING_BUFFER_TO_NEW_HANDLER; - return DoLoop(defer); + HoldController(std::move(controller)); + DoLoop(); } void InterceptingResourceHandler::OnResponseCompleted( const net::URLRequestStatus& status, - bool* defer) { + std::unique_ptr<ResourceController> controller) { if (state_ == State::PASS_THROUGH) { - LayeredResourceHandler::OnResponseCompleted(status, defer); + LayeredResourceHandler::OnResponseCompleted(status, std::move(controller)); return; } if (!new_handler_) { // Therer is only one ResourceHandler in this InterceptingResourceHandler. state_ = State::PASS_THROUGH; first_read_buffer_double_ = nullptr; - next_handler_->SetController(controller()); - next_handler_->OnResponseCompleted(status, defer); + next_handler_->OnResponseCompleted(status, std::move(controller)); return; } // There are two ResourceHandlers in this InterceptingResourceHandler. // |next_handler_| is the old handler and |new_handler_| is the new handler. // As written in the class comment, this class assumes that the old handler - // will not set |*defer| in OnResponseCompleted. - next_handler_->SetController(controller()); - next_handler_->OnResponseCompleted(status, defer); - DCHECK(!*defer); + // will immediately call Resume() in OnResponseCompleted. + bool was_resumed = false; + // TODO(mmenke): Get rid of NullResourceController and do something more + // reasonable. + next_handler_->OnResponseCompleted( + status, base::MakeUnique<NullResourceController>(&was_resumed)); + DCHECK(was_resumed); state_ = State::PASS_THROUGH; first_read_buffer_double_ = nullptr; - new_handler_->SetController(controller()); next_handler_ = std::move(new_handler_); - next_handler_->OnResponseCompleted(status, defer); -} - -void InterceptingResourceHandler::Cancel() { - DCHECK_NE(State::PASS_THROUGH, state_); - controller()->Cancel(); -} - -void InterceptingResourceHandler::CancelAndIgnore() { - DCHECK_NE(State::PASS_THROUGH, state_); - controller()->CancelAndIgnore(); -} - -void InterceptingResourceHandler::CancelWithError(int error_code) { - DCHECK_NE(State::PASS_THROUGH, state_); - controller()->CancelWithError(error_code); -} - -void InterceptingResourceHandler::Resume() { - DCHECK_NE(State::PASS_THROUGH, state_); - if (state_ == State::STARTING || - state_ == State::WAITING_FOR_ON_READ_COMPLETED) { - // Uninteresting Resume: just delegate to the original resource controller. - controller()->Resume(); - return; - } - - // Can't call DoLoop synchronously, as it may call into |next_handler_| - // synchronously, which is what called Resume(). - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&InterceptingResourceHandler::AdvanceState, - weak_ptr_factory_.GetWeakPtr())); + next_handler_->OnResponseCompleted(status, std::move(controller)); } void InterceptingResourceHandler::UseNewHandler( std::unique_ptr<ResourceHandler> new_handler, const std::string& payload_for_old_handler) { new_handler_ = std::move(new_handler); - new_handler_->SetController(this); + new_handler_->SetDelegate(delegate()); payload_for_old_handler_ = payload_for_old_handler; } -bool InterceptingResourceHandler::DoLoop(bool* defer) { - bool result = true; - do { +void InterceptingResourceHandler::DoLoop() { + DCHECK(!in_do_loop_); + DCHECK(!advance_to_next_state_); + + base::AutoReset<bool> auto_in_do_loop(&in_do_loop_, true); + advance_to_next_state_ = true; + + while (advance_to_next_state_) { + advance_to_next_state_ = false; + switch (state_) { case State::STARTING: case State::WAITING_FOR_ON_READ_COMPLETED: @@ -178,7 +194,7 @@ NOTREACHED(); break; case State::SENDING_ON_WILL_START_TO_NEW_HANDLER: - result = SendOnResponseStartedToNewHandler(defer); + SendOnResponseStartedToNewHandler(); break; case State::SENDING_ON_RESPONSE_STARTED_TO_NEW_HANDLER: if (first_read_buffer_double_) { @@ -190,105 +206,129 @@ // OnWillRead has not been called, so no special handling will be // needed from now on. state_ = State::PASS_THROUGH; - next_handler_->SetController(controller()); } + ResumeInternal(); + break; + case State::SWAPPING_HANDLERS: + SendOnResponseStartedToOldHandler(); break; case State::SENDING_PAYLOAD_TO_OLD_HANDLER: - result = SendPayloadToOldHandler(defer); + SendPayloadToOldHandler(); break; case State::SENDING_BUFFER_TO_NEW_HANDLER: - result = SendFirstReadBufferToNewHandler(defer); + SendFirstReadBufferToNewHandler(); break; } - } while (result && !*defer && - state_ != State::WAITING_FOR_ON_READ_COMPLETED && - state_ != State::PASS_THROUGH); - return result; -} - -bool InterceptingResourceHandler::SendPayloadToOldHandler(bool* defer) { - DCHECK_EQ(State::SENDING_PAYLOAD_TO_OLD_HANDLER, state_); - while (payload_bytes_written_ < payload_for_old_handler_.size()) { - scoped_refptr<net::IOBuffer> buffer; - int size = 0; - if (first_read_buffer_) { - // |first_read_buffer_| is a buffer gotten from |next_handler_| via - // OnWillRead. Use the buffer. - buffer = first_read_buffer_; - size = first_read_buffer_size_; - - first_read_buffer_ = nullptr; - first_read_buffer_size_ = 0; - } else { - if (!next_handler_->OnWillRead(&buffer, &size, -1)) - return false; - } - - size = std::min(size, static_cast<int>(payload_for_old_handler_.size() - - payload_bytes_written_)); - memcpy(buffer->data(), - payload_for_old_handler_.data() + payload_bytes_written_, size); - if (!next_handler_->OnReadCompleted(size, defer)) - return false; - payload_bytes_written_ += size; - if (*defer) - return true; } - - net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, 0); - if (payload_for_old_handler_.empty()) { - // If there is no payload, just finalize the request on the old handler. - status = net::URLRequestStatus::FromError(net::ERR_ABORTED); - } - next_handler_->OnResponseCompleted(status, defer); - DCHECK(!*defer); - - next_handler_ = std::move(new_handler_); - state_ = State::SENDING_ON_WILL_START_TO_NEW_HANDLER; - return next_handler_->OnWillStart(request()->url(), defer); } -bool InterceptingResourceHandler::SendOnResponseStartedToNewHandler( - bool* defer) { - state_ = State::SENDING_ON_RESPONSE_STARTED_TO_NEW_HANDLER; - return next_handler_->OnResponseStarted(response_.get(), defer); -} - -bool InterceptingResourceHandler::SendFirstReadBufferToNewHandler(bool* defer) { - DCHECK_EQ(state_, State::SENDING_BUFFER_TO_NEW_HANDLER); - - while (first_read_buffer_bytes_written_ < first_read_buffer_bytes_read_) { - scoped_refptr<net::IOBuffer> buf; - int size = 0; - if (!next_handler_->OnWillRead(&buf, &size, -1)) - return false; - size = std::min(size, static_cast<int>(first_read_buffer_bytes_read_ - - first_read_buffer_bytes_written_)); - memcpy(buf->data(), - first_read_buffer_double_->data() + first_read_buffer_bytes_written_, - size); - if (!next_handler_->OnReadCompleted(size, defer)) - return false; - first_read_buffer_bytes_written_ += size; - if (*defer) - return true; - } - - state_ = State::PASS_THROUGH; - first_read_buffer_double_ = nullptr; - next_handler_->SetController(controller()); - return true; -} - -void InterceptingResourceHandler::AdvanceState() { - bool defer = false; - if (!DoLoop(&defer)) { - controller()->Cancel(); +void InterceptingResourceHandler::ResumeInternal() { + DCHECK(has_controller()); + if (state_ == State::STARTING || + state_ == State::WAITING_FOR_ON_READ_COMPLETED || + state_ == State::PASS_THROUGH) { + // Uninteresting Resume: just delegate to the original resource controller. + Resume(); return; } - if (!defer) - controller()->Resume(); + // If called recusively from a DoLoop, advance state when returning to the + // loop. + if (in_do_loop_) { + DCHECK(!advance_to_next_state_); + advance_to_next_state_ = true; + return; + } + + // Can't call DoLoop synchronously, as it may call into |next_handler_| + // synchronously, which is what called Resume(). + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&InterceptingResourceHandler::DoLoop, + weak_ptr_factory_.GetWeakPtr())); +} + +void InterceptingResourceHandler::SendOnResponseStartedToOldHandler() { + state_ = State::SENDING_PAYLOAD_TO_OLD_HANDLER; + next_handler_->OnResponseStarted(response_.get(), + base::MakeUnique<Controller>(this)); +} + +void InterceptingResourceHandler::SendPayloadToOldHandler() { + DCHECK_EQ(State::SENDING_PAYLOAD_TO_OLD_HANDLER, state_); + if (payload_bytes_written_ == payload_for_old_handler_.size()) { + net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, 0); + if (payload_for_old_handler_.empty()) { + // If there is no payload, just finalize the request on the old handler. + status = net::URLRequestStatus::FromError(net::ERR_ABORTED); + } + bool was_resumed = false; + // TODO(mmenke): Get rid of NullResourceController and do something more + // reasonable. + next_handler_->OnResponseCompleted( + status, base::MakeUnique<NullResourceController>(&was_resumed)); + DCHECK(was_resumed); + + next_handler_ = std::move(new_handler_); + state_ = State::SENDING_ON_WILL_START_TO_NEW_HANDLER; + next_handler_->OnWillStart(request()->url(), + base::MakeUnique<Controller>(this)); + return; + } + + scoped_refptr<net::IOBuffer> buffer; + int size = 0; + if (first_read_buffer_) { + // |first_read_buffer_| is a buffer gotten from |next_handler_| via + // OnWillRead. Use the buffer. + buffer = first_read_buffer_; + size = first_read_buffer_size_; + + first_read_buffer_ = nullptr; + first_read_buffer_size_ = 0; + } else { + if (!next_handler_->OnWillRead(&buffer, &size, -1)) { + Cancel(); + return; + } + } + + size = std::min(size, static_cast<int>(payload_for_old_handler_.size() - + payload_bytes_written_)); + memcpy(buffer->data(), + payload_for_old_handler_.data() + payload_bytes_written_, size); + payload_bytes_written_ += size; + next_handler_->OnReadCompleted(size, base::MakeUnique<Controller>(this)); +} + +void InterceptingResourceHandler::SendOnResponseStartedToNewHandler() { + state_ = State::SENDING_ON_RESPONSE_STARTED_TO_NEW_HANDLER; + next_handler_->OnResponseStarted(response_.get(), + base::MakeUnique<Controller>(this)); +} + +void InterceptingResourceHandler::SendFirstReadBufferToNewHandler() { + DCHECK_EQ(state_, State::SENDING_BUFFER_TO_NEW_HANDLER); + + if (first_read_buffer_bytes_written_ == first_read_buffer_bytes_read_) { + state_ = State::PASS_THROUGH; + first_read_buffer_double_ = nullptr; + ResumeInternal(); + return; + } + + scoped_refptr<net::IOBuffer> buf; + int size = 0; + if (!next_handler_->OnWillRead(&buf, &size, -1)) { + Cancel(); + return; + } + size = std::min(size, static_cast<int>(first_read_buffer_bytes_read_ - + first_read_buffer_bytes_written_)); + memcpy(buf->data(), + first_read_buffer_double_->data() + first_read_buffer_bytes_written_, + size); + first_read_buffer_bytes_written_ += size; + next_handler_->OnReadCompleted(size, base::MakeUnique<Controller>(this)); } } // namespace content
diff --git a/content/browser/loader/intercepting_resource_handler.h b/content/browser/loader/intercepting_resource_handler.h index 7ae6b72..183a1aa 100644 --- a/content/browser/loader/intercepting_resource_handler.h +++ b/content/browser/loader/intercepting_resource_handler.h
@@ -24,6 +24,8 @@ namespace content { +class ResourceController; + // ResourceHandler that initiates special handling of the response if needed, // based on the response's MIME type (starts downloads, sends data to some // plugin types via a special channel). @@ -33,28 +35,24 @@ // - OnResponseStarted on |next_handler| never sets |*defer|. // - OnResponseCompleted on |next_handler| never sets |*defer|. class CONTENT_EXPORT InterceptingResourceHandler - : public LayeredResourceHandler, - public ResourceController { + : public LayeredResourceHandler { public: InterceptingResourceHandler(std::unique_ptr<ResourceHandler> next_handler, net::URLRequest* request); ~InterceptingResourceHandler() override; // ResourceHandler implementation: - void SetController(ResourceController* controller) override; - bool OnResponseStarted(ResourceResponse* response, bool* defer) override; + void OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, int* buf_size, int min_size) override; - bool OnReadCompleted(int bytes_read, bool* defer) override; - void OnResponseCompleted(const net::URLRequestStatus& status, - bool* defer) override; - - // ResourceController implementation: - void Cancel() override; - void CancelAndIgnore() override; - void CancelWithError(int error_code) override; - void Resume() override; + void OnReadCompleted(int bytes_read, + std::unique_ptr<ResourceController> controller) override; + void OnResponseCompleted( + const net::URLRequestStatus& status, + std::unique_ptr<ResourceController> controller) override; // Replaces the next handler with |new_handler|, sending // |payload_for_old_handler| to the old handler. Must be called after @@ -70,12 +68,20 @@ } private: + // ResourceController subclass that calls into the InterceptingResourceHandler + // on cancel/resume. + class Controller; + enum class State { // The InterceptingResourceHandler is waiting for the mime type of the // response to be identified, to check if the next handler should be // replaced with an appropriate one. STARTING, + // The InterceptingResourceHandler is starting the process of swapping + // handlers. + SWAPPING_HANDLERS, + // The InterceptingResourceHandler is sending the payload given via // UseNewHandler to the old handler and waiting for its completion via // Resume(). @@ -108,18 +114,15 @@ PASS_THROUGH, }; - // Runs necessary operations depending on |state_|. Returns false when an - // error happens, and set |*defer| to true if the operation continues upon - // return. - bool DoLoop(bool* defer); + // Runs necessary operations depending on |state_|. + void DoLoop(); - // The return value and |defer| has the same meaning as DoLoop. - bool SendPayloadToOldHandler(bool* defer); - bool SendFirstReadBufferToNewHandler(bool* defer); - bool SendOnResponseStartedToNewHandler(bool* defer); + void ResumeInternal(); - // Wraps calls to DoLoop. Resumes or Cancels underlying request, if needed. - void AdvanceState(); + void SendOnResponseStartedToOldHandler(); + void SendPayloadToOldHandler(); + void SendFirstReadBufferToNewHandler(); + void SendOnResponseStartedToNewHandler(); State state_ = State::STARTING; @@ -139,6 +142,14 @@ scoped_refptr<ResourceResponse> response_; + // Next two values are used to handle synchronous Resume calls without a + // PostTask. + + // True if the code is currently within a DoLoop. + bool in_do_loop_ = false; + // True if the request was resumed while |in_do_loop_| was true; + bool advance_to_next_state_ = false; + base::WeakPtrFactory<InterceptingResourceHandler> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(InterceptingResourceHandler);
diff --git a/content/browser/loader/layered_resource_handler.cc b/content/browser/loader/layered_resource_handler.cc index 133e492..050e7e1 100644 --- a/content/browser/loader/layered_resource_handler.cc +++ b/content/browser/loader/layered_resource_handler.cc
@@ -7,6 +7,7 @@ #include <utility> #include "base/logging.h" +#include "content/browser/loader/resource_controller.h" namespace content { @@ -18,35 +19,32 @@ LayeredResourceHandler::~LayeredResourceHandler() { } -void LayeredResourceHandler::SetController(ResourceController* controller) { - ResourceHandler::SetController(controller); - - // Pass the controller down to the next handler. This method is intended to - // be overriden by subclasses of LayeredResourceHandler that need to insert a - // different ResourceController. - - DCHECK(next_handler_.get()); - next_handler_->SetController(controller); +void LayeredResourceHandler::SetDelegate(Delegate* delegate) { + ResourceHandler::SetDelegate(delegate); + next_handler_->SetDelegate(delegate); } -bool LayeredResourceHandler::OnRequestRedirected( +void LayeredResourceHandler::OnRequestRedirected( const net::RedirectInfo& redirect_info, ResourceResponse* response, - bool* defer) { + std::unique_ptr<ResourceController> controller) { DCHECK(next_handler_.get()); - return next_handler_->OnRequestRedirected(redirect_info, response, defer); + next_handler_->OnRequestRedirected(redirect_info, response, + std::move(controller)); } -bool LayeredResourceHandler::OnResponseStarted(ResourceResponse* response, - bool* defer) { +void LayeredResourceHandler::OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) { DCHECK(next_handler_.get()); - return next_handler_->OnResponseStarted(response, defer); + next_handler_->OnResponseStarted(response, std::move(controller)); } -bool LayeredResourceHandler::OnWillStart(const GURL& url, - bool* defer) { +void LayeredResourceHandler::OnWillStart( + const GURL& url, + std::unique_ptr<ResourceController> controller) { DCHECK(next_handler_.get()); - return next_handler_->OnWillStart(url, defer); + next_handler_->OnWillStart(url, std::move(controller)); } bool LayeredResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, @@ -56,16 +54,18 @@ return next_handler_->OnWillRead(buf, buf_size, min_size); } -bool LayeredResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { +void LayeredResourceHandler::OnReadCompleted( + int bytes_read, + std::unique_ptr<ResourceController> controller) { DCHECK(next_handler_.get()); - return next_handler_->OnReadCompleted(bytes_read, defer); + next_handler_->OnReadCompleted(bytes_read, std::move(controller)); } void LayeredResourceHandler::OnResponseCompleted( const net::URLRequestStatus& status, - bool* defer) { + std::unique_ptr<ResourceController> controller) { DCHECK(next_handler_.get()); - next_handler_->OnResponseCompleted(status, defer); + next_handler_->OnResponseCompleted(status, std::move(controller)); } void LayeredResourceHandler::OnDataDownloaded(int bytes_downloaded) {
diff --git a/content/browser/loader/layered_resource_handler.h b/content/browser/loader/layered_resource_handler.h index f38ef8c..9e92741 100644 --- a/content/browser/loader/layered_resource_handler.h +++ b/content/browser/loader/layered_resource_handler.h
@@ -16,6 +16,8 @@ namespace content { +class ResourceController; + // A ResourceHandler that simply delegates all calls to a next handler. This // class is intended to be subclassed. class CONTENT_EXPORT LayeredResourceHandler : public ResourceHandler { @@ -25,18 +27,24 @@ ~LayeredResourceHandler() override; // ResourceHandler implementation: - void SetController(ResourceController* controller) override; - bool OnRequestRedirected(const net::RedirectInfo& redirect_info, - ResourceResponse* response, - bool* defer) override; - bool OnResponseStarted(ResourceResponse* response, bool* defer) override; - bool OnWillStart(const GURL& url, bool* defer) override; + void SetDelegate(Delegate* delegate) override; + void OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; + void OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; + void OnWillStart(const GURL& url, + std::unique_ptr<ResourceController> controller) override; bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, int* buf_size, int min_size) override; - bool OnReadCompleted(int bytes_read, bool* defer) override; - void OnResponseCompleted(const net::URLRequestStatus& status, - bool* defer) override; + void OnReadCompleted(int bytes_read, + std::unique_ptr<ResourceController> controller) override; + void OnResponseCompleted( + const net::URLRequestStatus& status, + std::unique_ptr<ResourceController> controller) override; void OnDataDownloaded(int bytes_downloaded) override; std::unique_ptr<ResourceHandler> next_handler_;
diff --git a/content/browser/loader/mime_sniffing_resource_handler.cc b/content/browser/loader/mime_sniffing_resource_handler.cc index 6c13e047..0ce78bf0 100644 --- a/content/browser/loader/mime_sniffing_resource_handler.cc +++ b/content/browser/loader/mime_sniffing_resource_handler.cc
@@ -18,6 +18,7 @@ #include "content/browser/download/download_resource_handler.h" #include "content/browser/download/download_stats.h" #include "content/browser/loader/intercepting_resource_handler.h" +#include "content/browser/loader/resource_controller.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/loader/resource_request_info_impl.h" #include "content/browser/loader/stream_resource_handler.h" @@ -65,6 +66,48 @@ } // namespace +class MimeSniffingResourceHandler::Controller : public ResourceController { + public: + explicit Controller(MimeSniffingResourceHandler* mime_handler) + : mime_handler_(mime_handler) {} + + void Resume() override { + MarkAsUsed(); + mime_handler_->ResumeInternal(); + } + + void Cancel() override { + MarkAsUsed(); + mime_handler_->Cancel(); + } + + void CancelAndIgnore() override { + MarkAsUsed(); + mime_handler_->CancelAndIgnore(); + } + + void CancelWithError(int error_code) override { + MarkAsUsed(); + mime_handler_->CancelWithError(error_code); + } + + private: + void MarkAsUsed() { +#if DCHECK_IS_ON() + DCHECK(!used_); + used_ = true; +#endif + } + +#if DCHECK_IS_ON() + bool used_ = false; +#endif + + MimeSniffingResourceHandler* mime_handler_; + + DISALLOW_COPY_AND_ASSIGN(Controller); +}; + MimeSniffingResourceHandler::MimeSniffingResourceHandler( std::unique_ptr<ResourceHandler> next_handler, ResourceDispatcherHostImpl* host, @@ -84,23 +127,18 @@ bytes_read_(0), intercepting_handler_(intercepting_handler), request_context_type_(request_context_type), + in_state_loop_(false), + advance_state_(false), weak_ptr_factory_(this) { } MimeSniffingResourceHandler::~MimeSniffingResourceHandler() {} -void MimeSniffingResourceHandler::SetController( - ResourceController* controller) { - ResourceHandler::SetController(controller); +void MimeSniffingResourceHandler::OnWillStart( + const GURL& url, + std::unique_ptr<ResourceController> controller) { + DCHECK(!has_controller()); - // Downstream handlers see the MimeSniffingResourceHandler as their - // ResourceController, which allows it to consume part or all of the resource - // response, and then later replay it to downstream handler. - DCHECK(next_handler_.get()); - next_handler_->SetController(this); -} - -bool MimeSniffingResourceHandler::OnWillStart(const GURL& url, bool* defer) { const char* accept_value = nullptr; switch (GetRequestInfo()->GetResourceType()) { case RESOURCE_TYPE_MAIN_FRAME: @@ -137,12 +175,15 @@ // The false parameter prevents overwriting an existing accept header value, // which is needed because JS can manually set an accept header on an XHR. request()->SetExtraRequestHeaderByName(kAcceptHeader, accept_value, false); - return next_handler_->OnWillStart(url, defer); + next_handler_->OnWillStart(url, std::move(controller)); } -bool MimeSniffingResourceHandler::OnResponseStarted(ResourceResponse* response, - bool* defer) { +void MimeSniffingResourceHandler::OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) { DCHECK_EQ(STATE_STARTING, state_); + DCHECK(!has_controller()); + response_ = response; state_ = STATE_BUFFERING; @@ -151,8 +192,10 @@ // the response, and so must be skipped for 304 responses. if (!(response_->head.headers.get() && response_->head.headers->response_code() == 304)) { - if (ShouldSniffContent()) - return true; + if (ShouldSniffContent()) { + controller->Resume(); + return; + } if (response_->head.mime_type.empty()) { // Ugg. The server told us not to sniff the content but didn't give us a @@ -168,7 +211,8 @@ } } - return ProcessState(defer); + HoldController(std::move(controller)); + AdvanceState(); } bool MimeSniffingResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, @@ -194,9 +238,15 @@ return true; } -bool MimeSniffingResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { - if (state_ == STATE_STREAMING) - return next_handler_->OnReadCompleted(bytes_read, defer); +void MimeSniffingResourceHandler::OnReadCompleted( + int bytes_read, + std::unique_ptr<ResourceController> controller) { + DCHECK(!has_controller()); + + if (state_ == STATE_STREAMING) { + next_handler_->OnReadCompleted(bytes_read, std::move(controller)); + return; + } DCHECK_EQ(state_, STATE_BUFFERING); bytes_read_ += bytes_read; @@ -213,23 +263,29 @@ // that is probably better than the current one. response_->head.mime_type.assign(new_type); - if (!made_final_decision && (bytes_read > 0)) - return true; + if (!made_final_decision && (bytes_read > 0)) { + controller->Resume(); + return; + } - return ProcessState(defer); + HoldController(std::move(controller)); + AdvanceState(); } void MimeSniffingResourceHandler::OnResponseCompleted( const net::URLRequestStatus& status, - bool* defer) { + std::unique_ptr<ResourceController> resource_controller) { // Upon completion, act like a pass-through handler in case the downstream // handler defers OnResponseCompleted. state_ = STATE_STREAMING; - next_handler_->OnResponseCompleted(status, defer); + next_handler_->OnResponseCompleted(status, std::move(resource_controller)); } -void MimeSniffingResourceHandler::Resume() { +void MimeSniffingResourceHandler::ResumeInternal() { + DCHECK_NE(state_, STATE_BUFFERING); + DCHECK(!advance_state_); + // If no information is currently being transmitted to downstream handlers, // they should not attempt to resume the request. if (state_ == STATE_BUFFERING) { @@ -237,10 +293,8 @@ return; } - // If the BufferingHandler is acting as a pass-through handler, just ask the - // upwards ResourceController to resume the request. - if (state_ == STATE_STARTING || state_ == STATE_STREAMING) { - controller()->Resume(); + if (in_state_loop_) { + advance_state_ = true; return; } @@ -252,83 +306,76 @@ weak_ptr_factory_.GetWeakPtr())); } -void MimeSniffingResourceHandler::Cancel() { - controller()->Cancel(); -} - -void MimeSniffingResourceHandler::CancelAndIgnore() { - controller()->CancelAndIgnore(); -} - -void MimeSniffingResourceHandler::CancelWithError(int error_code) { - controller()->CancelWithError(error_code); -} - void MimeSniffingResourceHandler::AdvanceState() { - bool defer = false; - if (!ProcessState(&defer)) { - Cancel(); - } else if (!defer) { - DCHECK_EQ(STATE_STREAMING, state_); - controller()->Resume(); - } -} + DCHECK(!in_state_loop_); + DCHECK(!advance_state_); -bool MimeSniffingResourceHandler::ProcessState(bool* defer) { - bool return_value = true; - while (!*defer && return_value && state_ != STATE_STREAMING) { + base::AutoReset<bool> auto_in_state_loop(&in_state_loop_, true); + advance_state_ = true; + while (advance_state_) { + advance_state_ = false; + switch (state_) { case STATE_BUFFERING: - return_value = MaybeIntercept(defer); + MaybeIntercept(); break; case STATE_INTERCEPTION_CHECK_DONE: - return_value = ReplayResponseReceived(defer); + ReplayResponseReceived(); break; case STATE_REPLAYING_RESPONSE_RECEIVED: - return_value = ReplayReadCompleted(defer); + ReplayReadCompleted(); break; + case STATE_STARTING: + case STATE_STREAMING: + in_state_loop_ = false; + Resume(); + return; default: NOTREACHED(); break; } } - return return_value; + + DCHECK(in_state_loop_); + in_state_loop_ = false; } -bool MimeSniffingResourceHandler::MaybeIntercept(bool* defer) { +void MimeSniffingResourceHandler::MaybeIntercept() { DCHECK_EQ(STATE_BUFFERING, state_); // If a request that can be intercepted failed the check for interception // step, it should be canceled. - if (!MaybeStartInterception(defer)) - return false; + if (!MaybeStartInterception()) + return; - if (!*defer) - state_ = STATE_INTERCEPTION_CHECK_DONE; - - return true; + state_ = STATE_INTERCEPTION_CHECK_DONE; + ResumeInternal(); } -bool MimeSniffingResourceHandler::ReplayResponseReceived(bool* defer) { +void MimeSniffingResourceHandler::ReplayResponseReceived() { DCHECK_EQ(STATE_INTERCEPTION_CHECK_DONE, state_); state_ = STATE_REPLAYING_RESPONSE_RECEIVED; - return next_handler_->OnResponseStarted(response_.get(), defer); + next_handler_->OnResponseStarted(response_.get(), + base::MakeUnique<Controller>(this)); } -bool MimeSniffingResourceHandler::ReplayReadCompleted(bool* defer) { +void MimeSniffingResourceHandler::ReplayReadCompleted() { DCHECK_EQ(STATE_REPLAYING_RESPONSE_RECEIVED, state_); state_ = STATE_STREAMING; - if (!read_buffer_.get()) - return true; + if (!read_buffer_.get()) { + ResumeInternal(); + return; + } - bool result = next_handler_->OnReadCompleted(bytes_read_, defer); + int bytes_read = bytes_read_; read_buffer_ = nullptr; read_buffer_size_ = 0; bytes_read_ = 0; - return result; + next_handler_->OnReadCompleted(bytes_read, + base::MakeUnique<Controller>(this)); } bool MimeSniffingResourceHandler::ShouldSniffContent() { @@ -359,7 +406,7 @@ return false; } -bool MimeSniffingResourceHandler::MaybeStartInterception(bool* defer) { +bool MimeSniffingResourceHandler::MaybeStartInterception() { if (!CanBeIntercepted()) return true; @@ -373,9 +420,9 @@ DCHECK(!info->allow_download()); bool handled_by_plugin; - if (!CheckForPluginHandler(defer, &handled_by_plugin)) + if (!CheckForPluginHandler(&handled_by_plugin)) return false; - if (handled_by_plugin || *defer) + if (handled_by_plugin) return true; } @@ -394,9 +441,9 @@ return true; bool handled_by_plugin; - if (!CheckForPluginHandler(defer, &handled_by_plugin)) + if (!CheckForPluginHandler(&handled_by_plugin)) return false; - if (handled_by_plugin || *defer) + if (handled_by_plugin) return true; } @@ -416,7 +463,6 @@ } bool MimeSniffingResourceHandler::CheckForPluginHandler( - bool* defer, bool* handled_by_plugin) { *handled_by_plugin = false; #if BUILDFLAG(ENABLE_PLUGINS) @@ -435,8 +481,8 @@ base::Bind(&MimeSniffingResourceHandler::OnPluginsLoaded, weak_ptr_factory_.GetWeakPtr())); request()->LogBlockedBy("MimeSniffingResourceHandler"); - *defer = true; - return true; + // Will complete asynchronously. + return false; } if (has_plugin && plugin.type != WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN) { @@ -452,8 +498,10 @@ std::unique_ptr<ResourceHandler> handler(host_->MaybeInterceptAsStream( plugin_path, request(), response_.get(), &payload)); if (handler) { - if (!CheckResponseIsNotProvisional()) + if (!CheckResponseIsNotProvisional()) { + Cancel(); return false; + } *handled_by_plugin = true; intercepting_handler_->UseNewHandler(std::move(handler), payload); }
diff --git a/content/browser/loader/mime_sniffing_resource_handler.h b/content/browser/loader/mime_sniffing_resource_handler.h index 0effee5e..73cef65f 100644 --- a/content/browser/loader/mime_sniffing_resource_handler.h +++ b/content/browser/loader/mime_sniffing_resource_handler.h
@@ -8,6 +8,7 @@ #include <string> #include <vector> +#include "base/auto_reset.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "content/browser/loader/layered_resource_handler.h" @@ -23,6 +24,7 @@ namespace content { class InterceptingResourceHandler; class PluginService; +class ResourceController; class ResourceDispatcherHostImpl; struct WebPluginInfo; @@ -39,8 +41,7 @@ // Accept header on the request based on its ResourceType, if one isn't already // present. class CONTENT_EXPORT MimeSniffingResourceHandler - : public LayeredResourceHandler, - public ResourceController { + : public LayeredResourceHandler { public: MimeSniffingResourceHandler(std::unique_ptr<ResourceHandler> next_handler, ResourceDispatcherHostImpl* host, @@ -51,6 +52,8 @@ ~MimeSniffingResourceHandler() override; private: + class Controller; + friend class MimeSniffingResourceHandlerTest; enum State { // Starting state of the MimeSniffingResourceHandler. In this state, it is @@ -80,21 +83,21 @@ }; // ResourceHandler implementation: - void SetController(ResourceController* controller) override; - bool OnWillStart(const GURL&, bool* defer) override; - bool OnResponseStarted(ResourceResponse* response, bool* defer) override; + void OnWillStart(const GURL&, + std::unique_ptr<ResourceController> controller) override; + void OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, int* buf_size, int min_size) override; - bool OnReadCompleted(int bytes_read, bool* defer) override; - void OnResponseCompleted(const net::URLRequestStatus& status, - bool* defer) override; + void OnReadCompleted(int bytes_read, + std::unique_ptr<ResourceController> controller) override; + void OnResponseCompleted( + const net::URLRequestStatus& status, + std::unique_ptr<ResourceController> controller) override; - // ResourceController implementation: - void Resume() override; - void Cancel() override; - void CancelAndIgnore() override; - void CancelWithError(int error_code) override; + void ResumeInternal(); // -------------------------------------------------------------------------- // The following methods replay the buffered data to the downstream @@ -104,16 +107,15 @@ // Used to advance through the states of the state machine. void AdvanceState(); - bool ProcessState(bool* defer); // Intercepts the request as a stream/download if needed. - bool MaybeIntercept(bool* defer); + void MaybeIntercept(); // Replays OnResponseStarted on the downstream handlers. - bool ReplayResponseReceived(bool* defer); + void ReplayResponseReceived(); // Replays OnReadCompleted on the downstreams handlers. - bool ReplayReadCompleted(bool* defer); + void ReplayReadCompleted(); // -------------------------------------------------------------------------- @@ -123,18 +125,20 @@ // Checks whether this request should be intercepted as a stream or a // download. If this is the case, sets up the new ResourceHandler that will be - // used for interception. Returns false if teh request should be cancelled, - // true otherwise. |defer| is set to true if the interception check needs to - // finish asynchronously. - bool MaybeStartInterception(bool* defer); + // used for interception. + // + // Returns true on synchronous success, false if the operation will need to + // complete asynchronously or failure. On failure, also cancels the request. + bool MaybeStartInterception(); // Determines whether a plugin will handle the current request. Returns false // if there is an error and the request should be cancelled and true - // otherwise. |defer| is set to true if plugin data is stale and needs to be - // refreshed before the request can be handled (in this case the function - // still returns true). If the request is directed to a plugin, - // |handled_by_plugin| is set to true. - bool CheckForPluginHandler(bool* defer, bool* handled_by_plugin); + // otherwise. If the request is directed to a plugin, |handled_by_plugin| is + // set to true. + // + // Returns true on synchronous success, false if the operation will need to + // complete asynchronously or failure. On failure, also cancels the request. + bool CheckForPluginHandler(bool* handled_by_plugin); // Whether this request is allowed to be intercepted as a download or a // stream. @@ -170,6 +174,14 @@ RequestContextType request_context_type_; + // True if current in an AdvanceState loop. Used to prevent re-entrancy and + // avoid an extra PostTask. + bool in_state_loop_; + // Set to true if Resume() is called while |in_state_loop_| is true. When + // returning to the parent AdvanceState loop, will synchronously advance to + // the next state when control returns to the AdvanceState loop. + bool advance_state_; + base::WeakPtrFactory<MimeSniffingResourceHandler> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(MimeSniffingResourceHandler);
diff --git a/content/browser/loader/mock_resource_loader.cc b/content/browser/loader/mock_resource_loader.cc index 1cd7748f..0003fd5 100644 --- a/content/browser/loader/mock_resource_loader.cc +++ b/content/browser/loader/mock_resource_loader.cc
@@ -17,103 +17,90 @@ namespace content { +class MockResourceLoader::TestResourceController : public ResourceController { + public: + explicit TestResourceController(base::WeakPtr<MockResourceLoader> mock_loader) + : mock_loader_(mock_loader) {} + ~TestResourceController() override {} + + void Resume() override { mock_loader_->OnResume(); } + + void Cancel() override { CancelWithError(net::ERR_ABORTED); } + + void CancelAndIgnore() override { + ADD_FAILURE() << "Unexpected CancelAndIgnore call."; + Cancel(); + } + + void CancelWithError(int error_code) override { + mock_loader_->OnCancel(error_code); + } + + base::WeakPtr<MockResourceLoader> mock_loader_; +}; + MockResourceLoader::MockResourceLoader(ResourceHandler* resource_handler) - : resource_handler_(resource_handler) { - resource_handler_->SetController(this); + : resource_handler_(resource_handler), weak_factory_(this) { + resource_handler_->SetDelegate(this); } MockResourceLoader::~MockResourceLoader() {} MockResourceLoader::Status MockResourceLoader::OnWillStart(const GURL& url) { + EXPECT_FALSE(weak_factory_.HasWeakPtrs()); EXPECT_EQ(Status::IDLE, status_); - status_ = Status::CALLING_HANDLER; - bool defer = false; - bool result = resource_handler_->OnWillStart(url, &defer); - // The second case isn't really allowed, but a number of classes do it - // anyways. - EXPECT_TRUE(status_ == Status::CALLING_HANDLER || - (result == false && status_ == Status::CANCELED)); - if (!result) { - // In the case of double-cancels, keep the old error code. - // TODO(mmenke): Once there are no more double cancel cases, remove this - // code. - if (status_ != Status::CANCELED) - error_code_ = net::ERR_ABORTED; - status_ = Status::CANCELED; - } else if (defer) { + status_ = Status::CALLING_HANDLER; + resource_handler_->OnWillStart(url, base::MakeUnique<TestResourceController>( + weak_factory_.GetWeakPtr())); + if (status_ == Status::CALLING_HANDLER) status_ = Status::CALLBACK_PENDING; - } else { - status_ = Status::IDLE; - } return status_; } MockResourceLoader::Status MockResourceLoader::OnRequestRedirected( const net::RedirectInfo& redirect_info, scoped_refptr<ResourceResponse> response) { + EXPECT_FALSE(weak_factory_.HasWeakPtrs()); EXPECT_EQ(Status::IDLE, status_); - status_ = Status::CALLING_HANDLER; - bool defer = false; + status_ = Status::CALLING_HANDLER; // Note that |this| does not hold onto |response|, to match ResourceLoader's // behavior. If |resource_handler_| wants to use |response| asynchronously, it // needs to hold onto its own pointer to it. - bool result = resource_handler_->OnRequestRedirected(redirect_info, - response.get(), &defer); - // The second case isn't really allowed, but a number of classes do it - // anyways. - EXPECT_TRUE(status_ == Status::CALLING_HANDLER || - (result == false && status_ == Status::CANCELED)); - if (!result) { - // In the case of double-cancels, keep the old error code. - if (status_ != Status::CANCELED) - error_code_ = net::ERR_ABORTED; - status_ = Status::CANCELED; - } else if (defer) { + resource_handler_->OnRequestRedirected( + redirect_info, response.get(), + base::MakeUnique<TestResourceController>(weak_factory_.GetWeakPtr())); + if (status_ == Status::CALLING_HANDLER) status_ = Status::CALLBACK_PENDING; - } else { - status_ = Status::IDLE; - } return status_; } MockResourceLoader::Status MockResourceLoader::OnResponseStarted( scoped_refptr<ResourceResponse> response) { + EXPECT_FALSE(weak_factory_.HasWeakPtrs()); EXPECT_EQ(Status::IDLE, status_); - status_ = Status::CALLING_HANDLER; - bool defer = false; + status_ = Status::CALLING_HANDLER; // Note that |this| does not hold onto |response|, to match ResourceLoader's // behavior. If |resource_handler_| wants to use |response| asynchronously, it // needs to hold onto its own pointer to it. - bool result = resource_handler_->OnResponseStarted(response.get(), &defer); - // The second case isn't really allowed, but a number of classes do it - // anyways. - EXPECT_TRUE(status_ == Status::CALLING_HANDLER || - (result == false && status_ == Status::CANCELED)); - if (!result) { - // In the case of double-cancels, keep the old error code. - if (status_ != Status::CANCELED) - error_code_ = net::ERR_ABORTED; - status_ = Status::CANCELED; - } else if (defer) { + resource_handler_->OnResponseStarted( + response.get(), + base::MakeUnique<TestResourceController>(weak_factory_.GetWeakPtr())); + if (status_ == Status::CALLING_HANDLER) status_ = Status::CALLBACK_PENDING; - } else { - status_ = Status::IDLE; - } return status_; } MockResourceLoader::Status MockResourceLoader::OnWillRead(int min_size) { + EXPECT_FALSE(weak_factory_.HasWeakPtrs()); EXPECT_EQ(Status::IDLE, status_); - EXPECT_FALSE(io_buffer_); - EXPECT_EQ(0, io_buffer_size_); status_ = Status::CALLING_HANDLER; - bool result = resource_handler_->OnWillRead(&io_buffer_, &io_buffer_size_, min_size); + // The second case isn't really allowed, but a number of classes do it // anyways. EXPECT_TRUE(status_ == Status::CALLING_HANDLER || @@ -136,6 +123,7 @@ MockResourceLoader::Status MockResourceLoader::OnReadCompleted( base::StringPiece bytes) { + EXPECT_FALSE(weak_factory_.HasWeakPtrs()); EXPECT_EQ(Status::IDLE, status_); EXPECT_LE(bytes.size(), static_cast<size_t>(io_buffer_size_)); @@ -143,29 +131,17 @@ std::copy(bytes.begin(), bytes.end(), io_buffer_->data()); io_buffer_ = nullptr; io_buffer_size_ = 0; - status_ = Status::CALLING_HANDLER; - - bool defer = false; - bool result = resource_handler_->OnReadCompleted(bytes.size(), &defer); - // The second case isn't really allowed, but a number of classes do it - // anyways. - EXPECT_TRUE(status_ == Status::CALLING_HANDLER || - (result == false && status_ == Status::CANCELED)); - if (!result) { - // In the case of double-cancels, keep the old error code. - if (status_ != Status::CANCELED) - error_code_ = net::ERR_ABORTED; - status_ = Status::CANCELED; - } else if (defer) { + resource_handler_->OnReadCompleted( + bytes.size(), + base::MakeUnique<TestResourceController>(weak_factory_.GetWeakPtr())); + if (status_ == Status::CALLING_HANDLER) status_ = Status::CALLBACK_PENDING; - } else { - status_ = Status::IDLE; - } return status_; } MockResourceLoader::Status MockResourceLoader::OnResponseCompleted( const net::URLRequestStatus& status) { + EXPECT_FALSE(weak_factory_.HasWeakPtrs()); // This should only happen while the ResourceLoader is idle or the request was // canceled. EXPECT_TRUE(status_ == Status::IDLE || @@ -175,15 +151,12 @@ io_buffer_ = nullptr; io_buffer_size_ = 0; status_ = Status::CALLING_HANDLER; - - bool defer = false; - resource_handler_->OnResponseCompleted(status, &defer); - EXPECT_EQ(Status::CALLING_HANDLER, status_); - if (defer) { + resource_handler_->OnResponseCompleted( + status, + base::MakeUnique<TestResourceController>(weak_factory_.GetWeakPtr())); + if (status_ == Status::CALLING_HANDLER) status_ = Status::CALLBACK_PENDING; - } else { - status_ = Status::IDLE; - } + EXPECT_NE(Status::CANCELED, status_); return status_; } @@ -198,14 +171,12 @@ io_buffer_size_ = 0; status_ = Status::CALLING_HANDLER; - bool defer = false; - resource_handler_->OnResponseCompleted(url_request_status, &defer); - EXPECT_EQ(Status::CALLING_HANDLER, status_); - if (defer) { + resource_handler_->OnResponseCompleted( + url_request_status, + base::MakeUnique<TestResourceController>(weak_factory_.GetWeakPtr())); + if (status_ == Status::CALLING_HANDLER) status_ = Status::CALLBACK_PENDING; - } else { - status_ = Status::IDLE; - } + EXPECT_NE(Status::CANCELED, status_); return status_; } @@ -219,35 +190,42 @@ EXPECT_TRUE(status_ == Status::IDLE || status_ == Status::CANCELED); } -void MockResourceLoader::Cancel() { - CancelWithError(net::ERR_ABORTED); +void MockResourceLoader::OutOfBandCancel(int error_code, bool tell_renderer) { + // Shouldn't be called in-band. + EXPECT_NE(Status::CALLING_HANDLER, status_); + + status_ = Status::CANCELED; + canceled_out_of_band_ = true; + + // To mimic real behavior, keep old error, in the case of double-cancel. + if (error_code_ == net::OK) + error_code_ = error_code; } -void MockResourceLoader::CancelAndIgnore() { - NOTREACHED(); -} - -void MockResourceLoader::CancelWithError(int error_code) { - EXPECT_LT(error_code, 0); - // Ignore double cancels. Classes shouldn't be doing this, as - // ResourceLoader doesn't really support it, but they do anywways. :( - // TODO(mmenke): Remove this check. - if (error_code_ != net::OK) +void MockResourceLoader::OnCancel(int error_code) { + // It's currently allowed to be canceled in-band after being cancelled + // out-of-band, so do nothing, unless the status is no longer CANCELED, which + // which case, OnResponseCompleted has already been called, and cancels aren't + // expected then. + // TODO(mmenke): Make CancelOutOfBand synchronously destroy the + // ResourceLoader. + if (canceled_out_of_band_ && status_ == Status::CANCELED) return; - // ResourceLoader really expects canceled not to be called synchronously, but - // a lot of code does it, so allow it. - // TODO(mmenke): Remove CALLING_HANDLER. + EXPECT_LT(error_code, 0); EXPECT_TRUE(status_ == Status::CALLBACK_PENDING || - status_ == Status::CALLING_HANDLER || status_ == Status::IDLE); + status_ == Status::CALLING_HANDLER); + status_ = Status::CANCELED; error_code_ = error_code; if (canceled_or_idle_run_loop_) canceled_or_idle_run_loop_->Quit(); } -void MockResourceLoader::Resume() { - EXPECT_EQ(Status::CALLBACK_PENDING, status_); +void MockResourceLoader::OnResume() { + EXPECT_TRUE(status_ == Status::CALLBACK_PENDING || + status_ == Status::CALLING_HANDLER); + status_ = Status::IDLE; if (canceled_or_idle_run_loop_) canceled_or_idle_run_loop_->Quit();
diff --git a/content/browser/loader/mock_resource_loader.h b/content/browser/loader/mock_resource_loader.h index a2272819..b3e962e8 100644 --- a/content/browser/loader/mock_resource_loader.h +++ b/content/browser/loader/mock_resource_loader.h
@@ -10,9 +10,11 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "base/run_loop.h" #include "base/strings/string_piece.h" #include "content/browser/loader/resource_controller.h" +#include "content/browser/loader/resource_handler.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" @@ -24,13 +26,12 @@ } namespace content { -class ResourceHandler; struct ResourceResponse; // Class that takes the place of the ResourceLoader for tests. It simplifies // testing ResourceHandlers by managing callbacks and performing basic sanity // checks. The test fixture is responsible for advancing states. -class MockResourceLoader : public ResourceController { +class MockResourceLoader : public ResourceHandler::Delegate { public: explicit MockResourceLoader(ResourceHandler* resource_handler); ~MockResourceLoader() override; @@ -85,22 +86,27 @@ int io_buffer_size() const { return io_buffer_size_; } private: - // ResourceController implementation. - void Cancel() override; - void CancelAndIgnore() override; - void CancelWithError(int error_code) override; - void Resume() override; + class TestResourceController; + + // ResourceHandler::Delegate implementation: + void OutOfBandCancel(int error_code, bool tell_renderer) override; + + void OnCancel(int error_code); + void OnResume(); ResourceHandler* const resource_handler_; Status status_ = Status::IDLE; int error_code_ = net::OK; + bool canceled_out_of_band_ = false; scoped_refptr<net::IOBuffer> io_buffer_; int io_buffer_size_ = 0; std::unique_ptr<base::RunLoop> canceled_or_idle_run_loop_; + base::WeakPtrFactory<MockResourceLoader> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(MockResourceLoader); };
diff --git a/content/browser/loader/mojo_async_resource_handler.cc b/content/browser/loader/mojo_async_resource_handler.cc index f1f0f4c..4d42b594 100644 --- a/content/browser/loader/mojo_async_resource_handler.cc +++ b/content/browser/loader/mojo_async_resource_handler.cc
@@ -145,16 +145,17 @@ rdh_->FinishedWithResourcesForRequest(request()); } -bool MojoAsyncResourceHandler::OnRequestRedirected( +void MojoAsyncResourceHandler::OnRequestRedirected( const net::RedirectInfo& redirect_info, ResourceResponse* response, - bool* defer) { + std::unique_ptr<ResourceController> controller) { // Unlike OnResponseStarted, OnRequestRedirected will NOT be preceded by // OnWillRead. + DCHECK(!has_controller()); DCHECK(!shared_writer_); - *defer = true; request()->LogBlockedBy("MojoAsyncResourceHandler"); + HoldController(std::move(controller)); did_defer_on_redirect_ = true; NetLogObserver::PopulateResponseInfo(request(), response); @@ -166,11 +167,13 @@ // and hopefully those will eventually all be owned by the browser. It's // possible this is still needed while renderer-owned ones exist. url_loader_client_->OnReceiveRedirect(redirect_info, response->head); - return true; } -bool MojoAsyncResourceHandler::OnResponseStarted(ResourceResponse* response, - bool* defer) { +void MojoAsyncResourceHandler::OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) { + DCHECK(!has_controller()); + if (upload_progress_tracker_) { upload_progress_tracker_->OnUploadCompleted(); upload_progress_tracker_ = nullptr; @@ -208,10 +211,13 @@ url_loader_client_->OnReceiveCachedMetadata( std::vector<uint8_t>(data, data + metadata->size())); } - return true; + + controller->Resume(); } -bool MojoAsyncResourceHandler::OnWillStart(const GURL& url, bool* defer) { +void MojoAsyncResourceHandler::OnWillStart( + const GURL& url, + std::unique_ptr<ResourceController> controller) { if (GetRequestInfo()->is_upload_progress_enabled() && request()->has_upload()) { upload_progress_tracker_ = CreateUploadProgressTracker( @@ -220,7 +226,7 @@ base::Unretained(this))); } - return true; + controller->Resume(); } bool MojoAsyncResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, @@ -228,6 +234,7 @@ int min_size) { DCHECK_EQ(-1, min_size); + // TODO(mmenke): Cancel with net::ERR_INSUFFICIENT_RESOURCES instead. if (!CheckForSufficientResource()) return false; @@ -274,12 +281,17 @@ return true; } -bool MojoAsyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { +void MojoAsyncResourceHandler::OnReadCompleted( + int bytes_read, + std::unique_ptr<ResourceController> controller) { + DCHECK(!has_controller()); DCHECK_GE(bytes_read, 0); DCHECK(buffer_); - if (!bytes_read) - return true; + if (!bytes_read) { + controller->Resume(); + return; + } const ResourceRequestInfoImpl* info = GetRequestInfo(); if (info->ShouldReportRawHeaders()) { @@ -299,26 +311,40 @@ // Couldn't allocate a buffer on the data pipe in OnWillRead. DCHECK_EQ(0u, buffer_bytes_read_); buffer_bytes_read_ = bytes_read; - if (!CopyReadDataToDataPipe(defer)) - return false; - if (*defer) { + bool defer = false; + if (!CopyReadDataToDataPipe(&defer)) { + controller->Cancel(); + return; + } + if (defer) { request()->LogBlockedBy("MojoAsyncResourceHandler"); did_defer_on_writing_ = true; + HoldController(std::move(controller)); + return; } - return true; + controller->Resume(); + return; } - if (EndWrite(bytes_read) != MOJO_RESULT_OK) - return false; + if (EndWrite(bytes_read) != MOJO_RESULT_OK) { + controller->Cancel(); + return; + } // Allocate a buffer for the next OnWillRead call here, because OnWillRead // doesn't have |defer| parameter. - if (!AllocateWriterIOBuffer(&buffer_, defer)) - return false; - if (*defer) { + bool defer = false; + if (!AllocateWriterIOBuffer(&buffer_, &defer)) { + controller->Cancel(); + return; + } + if (defer) { request()->LogBlockedBy("MojoAsyncResourceHandler"); did_defer_on_writing_ = true; + HoldController(std::move(controller)); + return; } - return true; + + controller->Resume(); } void MojoAsyncResourceHandler::OnDataDownloaded(int bytes_downloaded) { @@ -340,7 +366,7 @@ DCHECK(!did_defer_on_writing_); did_defer_on_redirect_ = false; request()->LogUnblocked(); - controller()->Resume(); + Resume(); } void MojoAsyncResourceHandler::SetPriority(net::RequestPriority priority, @@ -377,7 +403,7 @@ void MojoAsyncResourceHandler::OnResponseCompleted( const net::URLRequestStatus& status, - bool* defer) { + std::unique_ptr<ResourceController> controller) { // Ensure sending the final upload progress message here, since // OnResponseCompleted can be called without OnResponseStarted on cancellation // or error cases. @@ -419,6 +445,7 @@ request_complete_data.encoded_body_length = request()->GetRawBodyBytes(); url_loader_client_->OnComplete(request_complete_data); + controller->Resume(); } bool MojoAsyncResourceHandler::CopyReadDataToDataPipe(bool* defer) { @@ -474,13 +501,13 @@ if (rdh_->HasSufficientResourcesForRequest(request())) return true; - controller()->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); return false; } void MojoAsyncResourceHandler::OnWritable(MojoResult result) { if (!did_defer_on_writing_) return; + DCHECK(has_controller()); DCHECK(!did_defer_on_redirect_); did_defer_on_writing_ = false; @@ -489,13 +516,13 @@ // to the data pipe. DCHECK_GT(buffer_bytes_read_, 0u); if (!CopyReadDataToDataPipe(&did_defer_on_writing_)) { - controller()->CancelWithError(net::ERR_FAILED); + CancelWithError(net::ERR_FAILED); return; } } else { // Allocate a buffer for the next OnWillRead call here. if (!AllocateWriterIOBuffer(&buffer_, &did_defer_on_writing_)) { - controller()->CancelWithError(net::ERR_FAILED); + CancelWithError(net::ERR_FAILED); return; } } @@ -505,7 +532,7 @@ return; } request()->LogUnblocked(); - controller()->Resume(); + Resume(); } void MojoAsyncResourceHandler::Cancel() {
diff --git a/content/browser/loader/mojo_async_resource_handler.h b/content/browser/loader/mojo_async_resource_handler.h index 3b3e367..6d93f6ec 100644 --- a/content/browser/loader/mojo_async_resource_handler.h +++ b/content/browser/loader/mojo_async_resource_handler.h
@@ -36,6 +36,7 @@ } namespace content { +class ResourceController; class ResourceDispatcherHostImpl; struct ResourceResponse; @@ -60,17 +61,23 @@ ~MojoAsyncResourceHandler() override; // ResourceHandler implementation: - bool OnRequestRedirected(const net::RedirectInfo& redirect_info, - ResourceResponse* response, - bool* defer) override; - bool OnResponseStarted(ResourceResponse* response, bool* defer) override; - bool OnWillStart(const GURL& url, bool* defer) override; + void OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; + void OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; + void OnWillStart(const GURL& url, + std::unique_ptr<ResourceController> controller) override; bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, int* buf_size, int min_size) override; - bool OnReadCompleted(int bytes_read, bool* defer) override; - void OnResponseCompleted(const net::URLRequestStatus& status, - bool* defer) override; + void OnReadCompleted(int bytes_read, + std::unique_ptr<ResourceController> controller) override; + void OnResponseCompleted( + const net::URLRequestStatus& status, + std::unique_ptr<ResourceController> controller) override; void OnDataDownloaded(int bytes_downloaded) override; // mojom::URLLoader implementation:
diff --git a/content/browser/loader/mojo_async_resource_handler_unittest.cc b/content/browser/loader/mojo_async_resource_handler_unittest.cc index 6564586..35648fa 100644 --- a/content/browser/loader/mojo_async_resource_handler_unittest.cc +++ b/content/browser/loader/mojo_async_resource_handler_unittest.cc
@@ -544,7 +544,8 @@ ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); ASSERT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->OnWillRead(-1)); - EXPECT_EQ(net::ERR_INSUFFICIENT_RESOURCES, mock_loader_->error_code()); + // TODO(mmenke): Make this fail with net::ERR_INSUFFICIENT_RESOURCES. + EXPECT_EQ(net::ERR_ABORTED, mock_loader_->error_code()); EXPECT_EQ(1, rdh_.num_in_flight_requests_for_testing()); handler_ = nullptr; EXPECT_EQ(0, rdh_.num_in_flight_requests_for_testing());
diff --git a/content/browser/loader/navigation_resource_handler.cc b/content/browser/loader/navigation_resource_handler.cc index 20ee17d..15de158 100644 --- a/content/browser/loader/navigation_resource_handler.cc +++ b/content/browser/loader/navigation_resource_handler.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "base/bind.h" #include "base/logging.h" #include "content/browser/loader/navigation_url_loader_impl_core.h" #include "content/browser/loader/netlog_observer.h" @@ -53,51 +54,54 @@ } void NavigationResourceHandler::Cancel() { - controller()->Cancel(); - core_ = nullptr; + if (core_) { + core_ = nullptr; + OutOfBandCancel(net::ERR_ABORTED, true /* tell_renderer */); + } } void NavigationResourceHandler::FollowRedirect() { - controller()->Resume(); + Resume(); } void NavigationResourceHandler::ProceedWithResponse() { // Detach from the loader; at this point, the request is now owned by the // StreamHandle sent in OnResponseStarted. DetachFromCore(); - controller()->Resume(); + Resume(); } -void NavigationResourceHandler::SetController(ResourceController* controller) { - writer_.set_controller(controller); - ResourceHandler::SetController(controller); -} - -bool NavigationResourceHandler::OnRequestRedirected( +void NavigationResourceHandler::OnRequestRedirected( const net::RedirectInfo& redirect_info, ResourceResponse* response, - bool* defer) { + std::unique_ptr<ResourceController> controller) { DCHECK(core_); + DCHECK(!has_controller()); // TODO(davidben): Perform a CSP check here, and anything else that would have // been done renderer-side. NetLogObserver::PopulateResponseInfo(request(), response); response->head.encoded_data_length = request()->GetTotalReceivedBytes(); core_->NotifyRequestRedirected(redirect_info, response); - *defer = true; - return true; + + HoldController(std::move(controller)); } -bool NavigationResourceHandler::OnResponseStarted(ResourceResponse* response, - bool* defer) { +void NavigationResourceHandler::OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) { DCHECK(core_); + DCHECK(!has_controller()); ResourceRequestInfoImpl* info = GetRequestInfo(); StreamContext* stream_context = GetStreamContextForResourceContext(info->GetContext()); - writer_.InitializeStream(stream_context->registry(), - request()->url().GetOrigin()); + writer_.InitializeStream( + stream_context->registry(), request()->url().GetOrigin(), + base::Bind(&NavigationResourceHandler::OutOfBandCancel, + base::Unretained(this), net::ERR_ABORTED, + true /* tell_renderer */)); NetLogObserver::PopulateResponseInfo(request(), response); response->head.encoded_data_length = request()->raw_header_size(); @@ -136,34 +140,44 @@ // one part that wait on the NavigationThrottle to execute located between the // MIME sniffing and the ResourceThrotlle, and one part that write the // response to the stream being the leaf ResourceHandler. - if (!info->is_stream() && !info->IsDownload()) - *defer = true; - - return true; + if (info->is_stream() || info->IsDownload()) { + controller->Resume(); + } else { + HoldController(std::move(controller)); + } } -bool NavigationResourceHandler::OnWillStart(const GURL& url, bool* defer) { - return true; +void NavigationResourceHandler::OnWillStart( + const GURL& url, + std::unique_ptr<ResourceController> controller) { + DCHECK(!has_controller()); + controller->Resume(); } bool NavigationResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, int* buf_size, int min_size) { + DCHECK(!has_controller()); writer_.OnWillRead(buf, buf_size, min_size); return true; } -bool NavigationResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { - writer_.OnReadCompleted(bytes_read, defer); - return true; +void NavigationResourceHandler::OnReadCompleted( + int bytes_read, + std::unique_ptr<ResourceController> controller) { + DCHECK(!has_controller()); + writer_.OnReadCompleted(bytes_read, + base::Bind(&ResourceController::Resume, + base::Passed(std::move(controller)))); } void NavigationResourceHandler::OnResponseCompleted( const net::URLRequestStatus& status, - bool* defer) { + std::unique_ptr<ResourceController> controller) { // If the request has already committed, close the stream and leave it as-is. if (writer_.stream()) { writer_.Finalize(status.error()); + controller->Resume(); return; } @@ -173,6 +187,7 @@ status.error()); DetachFromCore(); } + controller->Resume(); } void NavigationResourceHandler::OnDataDownloaded(int bytes_downloaded) {
diff --git a/content/browser/loader/navigation_resource_handler.h b/content/browser/loader/navigation_resource_handler.h index 9608807..8b143819 100644 --- a/content/browser/loader/navigation_resource_handler.h +++ b/content/browser/loader/navigation_resource_handler.h
@@ -5,6 +5,8 @@ #ifndef CONTENT_BROWSER_LOADER_NAVIGATION_RESOURCE_HANDLER_H_ #define CONTENT_BROWSER_LOADER_NAVIGATION_RESOURCE_HANDLER_H_ +#include <memory> + #include "base/macros.h" #include "content/browser/loader/resource_handler.h" #include "content/browser/loader/stream_writer.h" @@ -15,6 +17,7 @@ namespace content { class NavigationURLLoaderImplCore; +class ResourceController; class ResourceDispatcherHostDelegate; struct SSLStatus; @@ -42,18 +45,23 @@ void ProceedWithResponse(); // ResourceHandler implementation. - void SetController(ResourceController* controller) override; - bool OnRequestRedirected(const net::RedirectInfo& redirect_info, - ResourceResponse* response, - bool* defer) override; - bool OnResponseStarted(ResourceResponse* response, bool* defer) override; - bool OnWillStart(const GURL& url, bool* defer) override; + void OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; + void OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; + void OnWillStart(const GURL& url, + std::unique_ptr<ResourceController> controller) override; bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, int* buf_size, int min_size) override; - bool OnReadCompleted(int bytes_read, bool* defer) override; - void OnResponseCompleted(const net::URLRequestStatus& status, - bool* defer) override; + void OnReadCompleted(int bytes_read, + std::unique_ptr<ResourceController> controller) override; + void OnResponseCompleted( + const net::URLRequestStatus& status, + std::unique_ptr<ResourceController> controller) override; void OnDataDownloaded(int bytes_downloaded) override; private:
diff --git a/content/browser/loader/null_resource_controller.cc b/content/browser/loader/null_resource_controller.cc new file mode 100644 index 0000000..05c3164 --- /dev/null +++ b/content/browser/loader/null_resource_controller.cc
@@ -0,0 +1,34 @@ +// 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/loader/null_resource_controller.h" + +#include "base/logging.h" + +namespace content { + +NullResourceController::NullResourceController(bool* was_resumed) + : was_resumed_(was_resumed) { + *was_resumed = false; +} + +NullResourceController::~NullResourceController() {} + +void NullResourceController::Cancel() { + DCHECK(!*was_resumed_); +} + +void NullResourceController::CancelAndIgnore() { + DCHECK(!*was_resumed_); +} + +void NullResourceController::CancelWithError(int error_code) { + DCHECK(!*was_resumed_); +} + +void NullResourceController::Resume() { + *was_resumed_ = true; +} + +} // namespace content
diff --git a/content/browser/loader/null_resource_controller.h b/content/browser/loader/null_resource_controller.h new file mode 100644 index 0000000..b9e6caf --- /dev/null +++ b/content/browser/loader/null_resource_controller.h
@@ -0,0 +1,36 @@ +// 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_BROWSER_LOADER_NULL_RESOURCE_CONTROLLER_H_ +#define CONTENT_BROWSER_LOADER_NULL_RESOURCE_CONTROLLER_H_ + +#include "base/macros.h" +#include "content/browser/loader/resource_controller.h" + +namespace content { + +// A pseudo-ResourceController that just tracks whether it was resumed or not. +// Only intended when calling into ResourceHandlers that either much resume the +// request, or when the consumer doesn't care if the request was resumed or not. +// TODO(mmenke): Modify consumers so they don't need to use this class. +class NullResourceController : public ResourceController { + public: + explicit NullResourceController(bool* was_resumed); + ~NullResourceController() override; + + // ResourceController implementation: + void Cancel() override; + void CancelAndIgnore() override; + void CancelWithError(int error_code) override; + void Resume() override; + + private: + bool* was_resumed_; + + DISALLOW_COPY_AND_ASSIGN(NullResourceController); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_LOADER_NULL_RESOURCE_CONTROLLER_H_
diff --git a/content/browser/loader/redirect_to_file_resource_handler.cc b/content/browser/loader/redirect_to_file_resource_handler.cc index b4018c8..f22fccc 100644 --- a/content/browser/loader/redirect_to_file_resource_handler.cc +++ b/content/browser/loader/redirect_to_file_resource_handler.cc
@@ -138,7 +138,6 @@ write_cursor_(0), writer_(NULL), next_buffer_size_(kInitialReadBufSize), - did_defer_(false), completed_during_write_(false), weak_factory_(this) {} @@ -156,22 +155,24 @@ create_temporary_file_stream_ = create_temporary_file_stream; } -bool RedirectToFileResourceHandler::OnResponseStarted( +void RedirectToFileResourceHandler::OnResponseStarted( ResourceResponse* response, - bool* defer) { + std::unique_ptr<ResourceController> controller) { DCHECK(writer_); response->head.download_file_path = writer_->path(); - return next_handler_->OnResponseStarted(response, defer); + next_handler_->OnResponseStarted(response, std::move(controller)); } -bool RedirectToFileResourceHandler::OnWillStart(const GURL& url, bool* defer) { +void RedirectToFileResourceHandler::OnWillStart( + const GURL& url, + std::unique_ptr<ResourceController> controller) { DCHECK(!writer_); // Defer starting the request until we have created the temporary file. // TODO(darin): This is sub-optimal. We should not delay starting the // network request like this. will_start_url_ = url; - did_defer_ = *defer = true; + HoldController(std::move(controller)); request()->LogBlockedBy("RedirectToFileResourceHandler"); if (create_temporary_file_stream_.is_null()) { CreateTemporaryFileStream( @@ -182,7 +183,6 @@ base::Bind(&RedirectToFileResourceHandler::DidCreateTemporaryFile, weak_factory_.GetWeakPtr())); } - return true; } bool RedirectToFileResourceHandler::OnWillRead( @@ -204,8 +204,9 @@ return true; } -bool RedirectToFileResourceHandler::OnReadCompleted(int bytes_read, - bool* defer) { +void RedirectToFileResourceHandler::OnReadCompleted( + int bytes_read, + std::unique_ptr<ResourceController> controller) { DCHECK(buf_write_pending_); buf_write_pending_ = false; @@ -220,28 +221,28 @@ next_buffer_size_ = std::min(next_buffer_size_ * 2, kMaxReadBufSize); } - bool success = WriteMore(); - - if (success && BufIsFull()) { - did_defer_ = *defer = true; - request()->LogBlockedBy("RedirectToFileResourceHandler"); + HoldController(std::move(controller)); + // WriteMore will resume the request if there's more buffer space. + if (!WriteMore()) { + CancelWithError(net::ERR_FAILED); + return; } - return success; + if (has_controller()) + request()->LogBlockedBy("RedirectToFileResourceHandler"); } void RedirectToFileResourceHandler::OnResponseCompleted( const net::URLRequestStatus& status, - bool* defer) { + std::unique_ptr<ResourceController> controller) { if (writer_ && writer_->is_writing()) { completed_during_write_ = true; completed_status_ = status; - did_defer_ = true; - *defer = true; + HoldController(std::move(controller)); request()->LogBlockedBy("RedirectToFileResourceHandler"); return; } - next_handler_->OnResponseCompleted(status, defer); + next_handler_->OnResponseCompleted(status, std::move(controller)); } int RedirectToFileResourceHandler::GetBufferSizeForTesting() const { @@ -253,25 +254,17 @@ std::unique_ptr<net::FileStream> file_stream, ShareableFileReference* deletable_file) { DCHECK(!writer_); + DCHECK(has_controller()); if (error_code != base::File::FILE_OK) { - controller()->CancelWithError(net::FileErrorToNetError(error_code)); + CancelWithError(net::FileErrorToNetError(error_code)); return; } writer_ = new Writer(this, std::move(file_stream), deletable_file); // Resume the request. - DCHECK(did_defer_); - bool defer = false; request()->LogUnblocked(); - if (!next_handler_->OnWillStart(will_start_url_, &defer)) { - controller()->Cancel(); - } else if (!defer) { - Resume(); - } else { - did_defer_ = false; - } - will_start_url_ = GURL(); + next_handler_->OnWillStart(std::move(will_start_url_), ReleaseController()); } void RedirectToFileResourceHandler::DidWriteToFile(int result) { @@ -279,6 +272,8 @@ if (result > 0) { next_handler_->OnDataDownloaded(result); write_cursor_ += result; + // WriteMore will resume the request if the request hasn't completed and + // there's more buffer space. failed = !WriteMore(); } else { failed = true; @@ -294,22 +289,25 @@ completed_status_ = net::URLRequestStatus(net::URLRequestStatus::CANCELED, net::ERR_FAILED); } - if (!completed_during_write_) - controller()->CancelWithError(net::ERR_FAILED); + if (!completed_during_write_) { + if (has_controller()) { + // If the write buffer is full, |this| has deferred the request, and + // can do an in-band cancel. + CancelWithError(net::ERR_FAILED); + } else { + OutOfBandCancel(net::ERR_FAILED, true /* tell_renderer */); + } + return; + } } if (completed_during_write_ && !writer_->is_writing()) { // Resume shutdown now that all data has been written to disk. Note that // this should run even in the |failed| case above, otherwise a failed write // leaves the handler stuck. - bool defer = false; + DCHECK(has_controller()); request()->LogUnblocked(); - next_handler_->OnResponseCompleted(completed_status_, &defer); - if (!defer) { - Resume(); - } else { - did_defer_ = false; - } + next_handler_->OnResponseCompleted(completed_status_, ReleaseController()); } } @@ -357,9 +355,9 @@ write_cursor_ += rv; } - // If the request was deferred to allow writing to the file, and the buffer is - // no longer full, resume the request. - if (did_defer_ && !completed_during_write_ && !BufIsFull()) { + // If the request was defered for a reason other than having been completed, + // and the buffer has space, resume the request. + if (has_controller() && !completed_during_write_ && !BufIsFull()) { request()->LogUnblocked(); Resume(); } @@ -374,10 +372,4 @@ return buf_->RemainingCapacity() <= (2 * net::kMaxBytesToSniff); } -void RedirectToFileResourceHandler::Resume() { - DCHECK(did_defer_); - did_defer_ = false; - controller()->Resume(); -} - } // namespace content
diff --git a/content/browser/loader/redirect_to_file_resource_handler.h b/content/browser/loader/redirect_to_file_resource_handler.h index bdd59e0..2de7c87 100644 --- a/content/browser/loader/redirect_to_file_resource_handler.h +++ b/content/browser/loader/redirect_to_file_resource_handler.h
@@ -32,6 +32,8 @@ namespace content { +class ResourceController; + // Redirects network data to a file. This is intended to be layered in front of // either the AsyncResourceHandler or the SyncResourceHandler. The downstream // resource handler does not see OnWillRead or OnReadCompleted calls. Instead, @@ -61,14 +63,19 @@ const CreateTemporaryFileStreamFunction& create_temporary_file_stream); // LayeredResourceHandler implementation: - bool OnResponseStarted(ResourceResponse* response, bool* defer) override; - bool OnWillStart(const GURL& url, bool* defer) override; + void OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; + void OnWillStart(const GURL& url, + std::unique_ptr<ResourceController> controller) override; bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, int* buf_size, int min_size) override; - bool OnReadCompleted(int bytes_read, bool* defer) override; - void OnResponseCompleted(const net::URLRequestStatus& status, - bool* defer) override; + void OnReadCompleted(int bytes_read, + std::unique_ptr<ResourceController> controller) override; + void OnResponseCompleted( + const net::URLRequestStatus& status, + std::unique_ptr<ResourceController> controller) override; // Returns the size of |buf_|, to make sure it's being increased as expected. int GetBufferSizeForTesting() const; @@ -81,9 +88,14 @@ // Called by RedirectToFileResourceHandler::Writer. void DidWriteToFile(int result); + // Attempts to write more data to the file, if possible. Returns false on + // error. Returns true if there's already a write in progress, all data was + // written successfully, or a new write was started that will complete + // asynchronously. Resumes the request if there's more data to read and more + // buffer space available. bool WriteMore(); + bool BufIsFull() const; - void Resume(); CreateTemporaryFileStreamFunction create_temporary_file_stream_; @@ -111,8 +123,6 @@ // was filled, up to a maximum size of 512k. int next_buffer_size_; - bool did_defer_; - bool completed_during_write_; GURL will_start_url_; net::URLRequestStatus completed_status_;
diff --git a/content/browser/loader/redirect_to_file_resource_handler_unittest.cc b/content/browser/loader/redirect_to_file_resource_handler_unittest.cc index e8736f2a..08ff0c9 100644 --- a/content/browser/loader/redirect_to_file_resource_handler_unittest.cc +++ b/content/browser/loader/redirect_to_file_resource_handler_unittest.cc
@@ -780,22 +780,14 @@ base::RunLoop().RunUntilIdle(); ASSERT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status()); - net::Error expected_error; - if (GetParam() == CompletionMode::ASYNC) { - expected_error = net::ERR_FAILED; - } else { - // An error cannot be passed up in the synchronous failure case. - // TODO(mmenke): That is changing, fix this. - expected_error = net::ERR_ABORTED; - } - EXPECT_EQ(expected_error, mock_loader_->error_code()); + EXPECT_EQ(net::ERR_FAILED, mock_loader_->error_code()); EXPECT_EQ(0, test_handler_->on_response_completed_called()); ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnResponseCompleted( - net::URLRequestStatus::FromError(expected_error))); + net::URLRequestStatus::FromError(net::ERR_FAILED))); EXPECT_EQ(0, test_handler_->total_bytes_downloaded()); EXPECT_FALSE(test_handler_->final_status().is_success()); - EXPECT_EQ(expected_error, test_handler_->final_status().error()); + EXPECT_EQ(net::ERR_FAILED, test_handler_->final_status().error()); } TEST_P(RedirectToFileResourceHandlerTest, SecondWriteFails) { @@ -824,22 +816,14 @@ base::RunLoop().RunUntilIdle(); ASSERT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status()); - net::Error expected_error; - if (GetParam() == CompletionMode::ASYNC) { - expected_error = net::ERR_FAILED; - } else { - // An error cannot be passed up in the synchronous failure case. - // TODO(mmenke): That is changing, fix this. - expected_error = net::ERR_ABORTED; - } - EXPECT_EQ(expected_error, mock_loader_->error_code()); + EXPECT_EQ(net::ERR_FAILED, mock_loader_->error_code()); EXPECT_EQ(0, test_handler_->on_response_completed_called()); ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnResponseCompleted( - net::URLRequestStatus::FromError(expected_error))); + net::URLRequestStatus::FromError(net::ERR_FAILED))); EXPECT_EQ(kFirstWriteSize, test_handler_->total_bytes_downloaded()); EXPECT_FALSE(test_handler_->final_status().is_success()); - EXPECT_EQ(expected_error, test_handler_->final_status().error()); + EXPECT_EQ(net::ERR_FAILED, test_handler_->final_status().error()); } INSTANTIATE_TEST_CASE_P(/* No prefix needed */,
diff --git a/content/browser/loader/resource_controller.h b/content/browser/loader/resource_controller.h index 98b2b72..85672556 100644 --- a/content/browser/loader/resource_controller.h +++ b/content/browser/loader/resource_controller.h
@@ -16,6 +16,8 @@ // to cancel load with any other error code. class CONTENT_EXPORT ResourceController { public: + virtual ~ResourceController() {} + virtual void Cancel() = 0; virtual void CancelAndIgnore() = 0; virtual void CancelWithError(int error_code) = 0; @@ -24,9 +26,6 @@ // deferred. Guaranteed not to call back into the ResourceHandler, or destroy // it, synchronously. virtual void Resume() = 0; - - protected: - virtual ~ResourceController() {} }; } // namespace content
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc index 5669449e..71577dc 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -53,6 +53,7 @@ #include "content/browser/loader/navigation_resource_handler.h" #include "content/browser/loader/navigation_resource_throttle.h" #include "content/browser/loader/navigation_url_loader_impl_core.h" +#include "content/browser/loader/null_resource_controller.h" #include "content/browser/loader/power_save_block_resource_throttle.h" #include "content/browser/loader/redirect_to_file_resource_handler.h" #include "content/browser/loader/resource_loader.h" @@ -2345,12 +2346,14 @@ // status -- it has no effect beyond this, since the request hasn't started. request->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); - bool defer = false; - handler->OnResponseCompleted(request->status(), &defer); - if (defer) { - // TODO(darin): The handler is not ready for us to kill the request. Oops! - NOTREACHED(); - } + bool was_resumed = false; + // TODO(mmenke): Get rid of NullResourceController and do something more + // reasonable. + handler->OnResponseCompleted( + request->status(), + base::MakeUnique<NullResourceController>(&was_resumed)); + // TODO(darin): The handler is not ready for us to kill the request. Oops! + DCHECK(was_resumed); IncrementOutstandingRequestsMemory(-1, *info);
diff --git a/content/browser/loader/resource_dispatcher_host_unittest.cc b/content/browser/loader/resource_dispatcher_host_unittest.cc index 3d8939b..5cf4c79 100644 --- a/content/browser/loader/resource_dispatcher_host_unittest.cc +++ b/content/browser/loader/resource_dispatcher_host_unittest.cc
@@ -2431,14 +2431,15 @@ for (size_t i = 0; i < kMaxRequestsPerProcess; ++i) CheckSuccessfulRequest(msgs[i], net::URLRequestTestJob::test_data_2()); + // TODO(mmenke): These should be failing with ERR_INSUFFICIENT_RESOURCES. + // Update OnWillRead to use a ResourceController so it can fail with different + // error codes. CheckFailedRequest(msgs[kMaxRequestsPerProcess + 0], - net::URLRequestTestJob::test_data_2(), - net::ERR_INSUFFICIENT_RESOURCES); + net::URLRequestTestJob::test_data_2(), net::ERR_ABORTED); CheckSuccessfulRequest(msgs[kMaxRequestsPerProcess + 1], net::URLRequestTestJob::test_data_2()); CheckFailedRequest(msgs[kMaxRequestsPerProcess + 2], - net::URLRequestTestJob::test_data_2(), - net::ERR_INSUFFICIENT_RESOURCES); + net::URLRequestTestJob::test_data_2(), net::ERR_ABORTED); second_filter->OnChannelClosing(); third_filter->OnChannelClosing();
diff --git a/content/browser/loader/resource_handler.cc b/content/browser/loader/resource_handler.cc index 896809a..961934d 100644 --- a/content/browser/loader/resource_handler.cc +++ b/content/browser/loader/resource_handler.cc
@@ -8,15 +8,48 @@ namespace content { +ResourceHandler::Delegate::Delegate() {} + +ResourceHandler::Delegate::~Delegate() {} + +void ResourceHandler::SetDelegate(Delegate* delegate) { + delegate_ = delegate; +} + ResourceHandler::~ResourceHandler() {} ResourceHandler::ResourceHandler(net::URLRequest* request) - : controller_(NULL), - request_(request) { + : request_(request) {} + +void ResourceHandler::HoldController( + std::unique_ptr<ResourceController> controller) { + controller_ = std::move(controller); } -void ResourceHandler::SetController(ResourceController* controller) { - controller_ = controller; +std::unique_ptr<ResourceController> ResourceHandler::ReleaseController() { + DCHECK(controller_); + + return std::move(controller_); +} + +void ResourceHandler::Resume() { + ReleaseController()->Resume(); +} + +void ResourceHandler::Cancel() { + ReleaseController()->Cancel(); +} + +void ResourceHandler::CancelAndIgnore() { + ReleaseController()->CancelAndIgnore(); +} + +void ResourceHandler::CancelWithError(int error_code) { + ReleaseController()->CancelWithError(error_code); +} + +void ResourceHandler::OutOfBandCancel(int error_code, bool tell_renderer) { + delegate_->OutOfBandCancel(error_code, tell_renderer); } ResourceRequestInfoImpl* ResourceHandler::GetRequestInfo() const {
diff --git a/content/browser/loader/resource_handler.h b/content/browser/loader/resource_handler.h index a6cc6822..350f9543 100644 --- a/content/browser/loader/resource_handler.h +++ b/content/browser/loader/resource_handler.h
@@ -12,12 +12,14 @@ #ifndef CONTENT_BROWSER_LOADER_RESOURCE_HANDLER_H_ #define CONTENT_BROWSER_LOADER_RESOURCE_HANDLER_H_ +#include <memory> #include <string> #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/threading/non_thread_safe.h" +#include "content/browser/loader/resource_controller.h" #include "content/common/content_export.h" class GURL; @@ -30,7 +32,6 @@ } // namespace net namespace content { -class ResourceController; class ResourceMessageFilter; class ResourceRequestInfoImpl; struct ResourceResponse; @@ -38,35 +39,62 @@ // The resource dispatcher host uses this interface to process network events // for an URLRequest instance. A ResourceHandler's lifetime is bound to its // associated URLRequest. +// +// No ResourceHandler method other than OnWillRead will ever be called +// synchronously when it calls into the ResourceController passed in to it, +// either to resume or cancel the request. class CONTENT_EXPORT ResourceHandler : public NON_EXPORTED_BASE(base::NonThreadSafe) { public: virtual ~ResourceHandler(); - // Sets the controller for this handler. - virtual void SetController(ResourceController* controller); + // Used to allow a ResourceHandler to cancel the request out of band, when it + // may not have a ResourceController. + class CONTENT_EXPORT Delegate { + protected: + Delegate(); + virtual ~Delegate(); - // The request was redirected to a new URL. |*defer| has an initial value of - // false. Set |*defer| to true to defer the redirect. The redirect may be - // followed later on via ResourceDispatcherHost::FollowDeferredRedirect. If - // the handler returns false, then the request is cancelled. - virtual bool OnRequestRedirected(const net::RedirectInfo& redirect_info, - ResourceResponse* response, - bool* defer) = 0; + private: + friend class ResourceHandler; - // Response headers and meta data are available. If the handler returns - // false, then the request is cancelled. Set |*defer| to true to defer - // processing of the response. Call ResourceDispatcherHostImpl:: - // ResumeDeferredRequest to continue processing the response. - virtual bool OnResponseStarted(ResourceResponse* response, bool* defer) = 0; + // Cancels the request when the class does not currently have ownership of + // the ResourceController. + // |error_code| indicates the reason for the cancellation, and + // |tell_renderer| whether the renderer needs to be informed of the + // cancellation. + virtual void OutOfBandCancel(int error_code, bool tell_renderer) = 0; + + DISALLOW_COPY_AND_ASSIGN(Delegate); + }; + + // Sets the ResourceHandler::Delegate, which handles out-of-band cancellation. + virtual void SetDelegate(Delegate* delegate); + + // The request was redirected to a new URL. The request will not continue + // until one of |controller|'s resume or cancellation methods is invoked. + // |response| may be destroyed as soon as the method returns, so if the + // ResourceHandler wants to continue to use it, it must maintain a reference + // to it. + virtual void OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) = 0; + + // Response headers and metadata are available. The request will not continue + // until one of |controller|'s resume or cancellation methods is invoked. + // |response| may be destroyed as soon as the method returns, so if the + // ResourceHandler wants to continue to use it, it must maintain a reference + // to it. + virtual void OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) = 0; // Called before the net::URLRequest (whose url is |url|) is to be started. - // If the handler returns false, then the request is cancelled. Otherwise if - // the return value is true, the ResourceHandler can delay the request from - // starting by setting |*defer = true|. A deferred request will not have - // called net::URLRequest::Start(), and will not resume until someone calls - // ResourceDispatcherHost::StartDeferredRequest(). - virtual bool OnWillStart(const GURL& url, bool* defer) = 0; + // The request will not continue until one of |controller|'s resume or + // cancellation methods is invoked. + virtual void OnWillStart(const GURL& url, + std::unique_ptr<ResourceController> controller) = 0; // Data will be read for the response. Upon success, this method places the // size and address of the buffer where the data is to be written in its @@ -76,24 +104,31 @@ // not be recycled and may potentially outlive the handler. If |min_size| is // not -1, it is the minimum size of the returned buffer. // + // Unlike other methods, may be called synchronously on Resume, for + // performance reasons. + // // If the handler returns false, then the request is cancelled. Otherwise, // once data is available, OnReadCompleted will be called. + // TODO(mmenke): Make this method use a ResourceController, and allow it to + // succeed asynchronously. virtual bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, int* buf_size, int min_size) = 0; // Data (*bytes_read bytes) was written into the buffer provided by - // OnWillRead. A return value of false cancels the request, true continues - // reading data. Set |*defer| to true to defer reading more response data. - // Call controller()->Resume() to continue reading response data. A zero - // |bytes_read| signals that no further data is available. - virtual bool OnReadCompleted(int bytes_read, bool* defer) = 0; + // OnWillRead. The request will not continue until one of |controller|'s + // resume or cancellation methods is invoked. A zero |bytes_read| signals + // that no further data will be received. + virtual void OnReadCompleted( + int bytes_read, + std::unique_ptr<ResourceController> controller) = 0; - // The response is complete. The final response status is given. Set - // |*defer| to true to defer destruction to a later time. Otherwise, the - // request will be destroyed upon return. - virtual void OnResponseCompleted(const net::URLRequestStatus& status, - bool* defer) = 0; + // The response is complete. The final response status is given. The request + // will not be deleted until controller's Resume() method is invoked. It is + // illegal to use its cancellation methods. + virtual void OnResponseCompleted( + const net::URLRequestStatus& status, + std::unique_ptr<ResourceController> controller) = 0; // This notification is synthesized by the RedirectToFileResourceHandler // to indicate progress of 'download_to_file' requests. OnReadCompleted @@ -102,9 +137,34 @@ virtual void OnDataDownloaded(int bytes_downloaded) = 0; protected: - ResourceHandler(net::URLRequest* request); + explicit ResourceHandler(net::URLRequest* request); - ResourceController* controller() const { return controller_; } + // Convenience methods for managing a ResourceHandler's controller in the + // async completion case. These ensure that the controller is nullptr after + // being invoked, which allows for DCHECKing on it and better crashes on + // calling into deleted objects. + + // Passes ownership of |controller| to |this|. Nothing is done with the + // |controller| automatically. + void HoldController(std::unique_ptr<ResourceController> controller); + + // Returns ownership of the ResourceController previously passed in to + // HoldController. + std::unique_ptr<ResourceController> ReleaseController(); + + bool has_controller() const { return !!controller_; } + + // These call the corresponding methods on the ResourceController previously + // passed to HoldController and then destroy it. + void Resume(); + void Cancel(); + void CancelAndIgnore(); + void CancelWithError(int error_code); + + // Cancels the request when the class does not currently have ownership of the + // ResourceController. + void OutOfBandCancel(int error_code, bool tell_renderer); + net::URLRequest* request() const { return request_; } // Convenience functions. @@ -112,9 +172,12 @@ int GetRequestID() const; ResourceMessageFilter* GetFilter() const; + Delegate* delegate() { return delegate_; } + private: - ResourceController* controller_; net::URLRequest* request_; + Delegate* delegate_; + std::unique_ptr<ResourceController> controller_; DISALLOW_COPY_AND_ASSIGN(ResourceHandler); };
diff --git a/content/browser/loader/resource_loader.cc b/content/browser/loader/resource_loader.cc index 241727b..02d24a8 100644 --- a/content/browser/loader/resource_loader.cc +++ b/content/browser/loader/resource_loader.cc
@@ -9,6 +9,7 @@ #include "base/callback_helpers.h" #include "base/command_line.h" #include "base/location.h" +#include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/profiler/scoped_tracker.h" #include "base/single_thread_task_runner.h" @@ -17,6 +18,7 @@ #include "content/browser/appcache/appcache_interceptor.h" #include "content/browser/child_process_security_policy_impl.h" #include "content/browser/loader/detachable_resource_handler.h" +#include "content/browser/loader/resource_controller.h" #include "content/browser/loader/resource_handler.h" #include "content/browser/loader/resource_loader_delegate.h" #include "content/browser/loader/resource_request_info_impl.h" @@ -130,6 +132,88 @@ } // namespace +class ResourceLoader::Controller : public ResourceController { + public: + explicit Controller(ResourceLoader* resource_loader) + : resource_loader_(resource_loader){}; + + ~Controller() override {} + + // ResourceController implementation: + void Resume() override { + MarkAsUsed(); + resource_loader_->Resume(true /* called_from_resource_controller */); + } + + void Cancel() override { + MarkAsUsed(); + resource_loader_->Cancel(); + } + + void CancelAndIgnore() override { + MarkAsUsed(); + resource_loader_->CancelAndIgnore(); + } + + void CancelWithError(int error_code) override { + MarkAsUsed(); + resource_loader_->CancelWithError(error_code); + } + + private: + void MarkAsUsed() { +#if DCHECK_IS_ON() + DCHECK(!used_); + used_ = true; +#endif + } + + ResourceLoader* const resource_loader_; + +#if DCHECK_IS_ON() + // Set to true once one of the ResourceContoller methods has been invoked. + bool used_ = false; +#endif + + DISALLOW_COPY_AND_ASSIGN(Controller); +}; + +// Helper class. Sets the stage of a ResourceLoader to DEFERRED_SYNC on +// construction, and on destruction does one of the following: +// 1) If the ResourceLoader has a deferred stage of DEFERRED_NONE, sets the +// ResourceLoader's stage to the stage specified on construction and resumes it. +// 2) If the ResourceLoader still has a deferred stage of DEFERRED_SYNC, sets +// the ResourceLoader's stage to the stage specified on construction. The +// ResourceLoader will be resumed at some point in the future. +class ResourceLoader::ScopedDeferral { + public: + ScopedDeferral(ResourceLoader* resource_loader, + ResourceLoader::DeferredStage deferred_stage) + : resource_loader_(resource_loader), deferred_stage_(deferred_stage) { + resource_loader_->deferred_stage_ = DEFERRED_SYNC; + } + + ~ScopedDeferral() { + DeferredStage old_deferred_stage = resource_loader_->deferred_stage_; + // On destruction, either the stage is still DEFERRED_SYNC, or Resume() was + // called once, and it advanced to DEFERRED_NONE. + DCHECK(old_deferred_stage == DEFERRED_NONE || + old_deferred_stage == DEFERRED_SYNC) + << old_deferred_stage; + resource_loader_->deferred_stage_ = deferred_stage_; + // If Resume() was called, it just advanced the state without doing + // anything. Go ahead and resume the request now. + if (old_deferred_stage == DEFERRED_NONE) + resource_loader_->Resume(false /* called_from_resource_controller */); + } + + private: + ResourceLoader* const resource_loader_; + const DeferredStage deferred_stage_; + + DISALLOW_COPY_AND_ASSIGN(ScopedDeferral); +}; + ResourceLoader::ResourceLoader(std::unique_ptr<net::URLRequest> request, std::unique_ptr<ResourceHandler> handler, ResourceLoaderDelegate* delegate) @@ -143,7 +227,7 @@ times_cancelled_after_request_start_(0), weak_ptr_factory_(this) { request_->set_delegate(this); - handler_->SetController(this); + handler_->SetDelegate(this); } ResourceLoader::~ResourceLoader() { @@ -157,20 +241,11 @@ } void ResourceLoader::StartRequest() { - // Give the handler a chance to delay the URLRequest from being started. - bool defer_start = false; - if (!handler_->OnWillStart(request_->url(), &defer_start)) { - Cancel(); - return; - } - TRACE_EVENT_WITH_FLOW0("loading", "ResourceLoader::StartRequest", this, TRACE_EVENT_FLAG_FLOW_OUT); - if (defer_start) { - deferred_stage_ = DEFERRED_START; - } else { - StartRequestInternal(); - } + + ScopedDeferral scoped_deferral(this, DEFERRED_START); + handler_->OnWillStart(request_->url(), base::MakeUnique<Controller>(this)); } void ResourceLoader::CancelRequest(bool from_renderer) { @@ -239,6 +314,10 @@ login_delegate_ = NULL; } +void ResourceLoader::OutOfBandCancel(int error_code, bool tell_renderer) { + CancelRequestInternal(error_code, !tell_renderer); +} + void ResourceLoader::OnReceivedRedirect(net::URLRequest* unused, const net::RedirectInfo& redirect_info, bool* defer) { @@ -271,16 +350,20 @@ scoped_refptr<ResourceResponse> response = new ResourceResponse(); PopulateResourceResponse(info, request_.get(), response.get()); delegate_->DidReceiveRedirect(this, redirect_info.new_url, response.get()); - if (!handler_->OnRequestRedirected(redirect_info, response.get(), defer)) { - Cancel(); - } else if (*defer) { - deferred_stage_ = DEFERRED_REDIRECT; // Follow redirect when resumed. - DCHECK(deferred_redirect_url_.is_empty()); + + // Can't used ScopedDeferral here, because on sync completion, need to set + // |defer| to false instead of calling back into the URLRequest. + deferred_stage_ = DEFERRED_SYNC; + handler_->OnRequestRedirected(redirect_info, response.get(), + base::MakeUnique<Controller>(this)); + if (is_deferred()) { + *defer = true; deferred_redirect_url_ = redirect_info.new_url; - } else if (delegate_->HandleExternalProtocol(this, redirect_info.new_url)) { - // The request is complete so we can remove it. - CancelAndIgnore(); - return; + deferred_stage_ = DEFERRED_REDIRECT; + } else { + *defer = false; + if (delegate_->HandleExternalProtocol(this, redirect_info.new_url)) + CancelAndIgnore(); } } @@ -344,14 +427,6 @@ } CompleteResponseStarted(); - - // If the handler deferred the request, it will resume the request later. If - // the request was cancelled, the request will call back into |this| with a - // bogus read completed error. - if (is_deferred() || !request_->status().is_success()) - return; - - ReadMore(false); // Read the first chunk. } void ResourceLoader::OnReadCompleted(net::URLRequest* unused, int bytes_read) { @@ -368,25 +443,6 @@ } CompleteRead(bytes_read); - - // If the handler cancelled or deferred the request, do not continue - // processing the read. If canceled, either the request will call into |this| - // with a bogus read error, or, if the request was completed, a task posted - // from ResourceLoader::CancelREquestInternal will run OnResponseCompleted. - if (is_deferred() || !request_->status().is_success()) - return; - - if (bytes_read > 0) { - ReadMore(true); // Read the next chunk. - } else { - // TODO(darin): Remove ScopedTracker below once crbug.com/475761 is fixed. - tracked_objects::ScopedTracker tracking_profile( - FROM_HERE_WITH_EXPLICIT_FUNCTION("475761 ResponseCompleted()")); - - // URLRequest reported an EOF. Call ResponseCompleted. - DCHECK_EQ(0, bytes_read); - ResponseCompleted(); - } } void ResourceLoader::CancelSSLRequest(int error, @@ -432,7 +488,7 @@ request_->CancelWithError(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED); } -void ResourceLoader::Resume() { +void ResourceLoader::Resume(bool called_from_resource_controller) { DCHECK(!is_transferring_); DeferredStage stage = deferred_stage_; @@ -441,27 +497,54 @@ case DEFERRED_NONE: NOTREACHED(); break; + case DEFERRED_SYNC: + DCHECK(called_from_resource_controller); + // Request will be resumed when the stack unwinds. + break; case DEFERRED_START: + // URLRequest::Start completes asynchronously, so starting the request now + // won't result in synchronously calling into a ResourceHandler, if this + // was called from Resume(). StartRequestInternal(); break; case DEFERRED_REDIRECT: + // URLRequest::Start completes asynchronously, so starting the request now + // won't result in synchronously calling into a ResourceHandler, if this + // was called from Resume(). FollowDeferredRedirectInternal(); break; case DEFERRED_READ: - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&ResourceLoader::ResumeReading, - weak_ptr_factory_.GetWeakPtr())); + if (called_from_resource_controller) { + // TODO(mmenke): Call ReadMore instead? Strange that this is the only + // path which calls different methods, depending on the path. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&ResourceLoader::ResumeReading, + weak_ptr_factory_.GetWeakPtr())); + } else { + // If this was called as a result of a handler succeeding synchronously, + // force the result of the next read to be handled asynchronously, to + // avoid blocking the IO thread. + ReadMore(true /* handle_result_asynchronously */); + } break; case DEFERRED_RESPONSE_COMPLETE: - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&ResourceLoader::ResponseCompleted, - weak_ptr_factory_.GetWeakPtr())); + if (called_from_resource_controller) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&ResourceLoader::ResponseCompleted, + weak_ptr_factory_.GetWeakPtr())); + } else { + ResponseCompleted(); + } break; case DEFERRED_FINISH: - // Delay self-destruction since we don't know how we were reached. - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&ResourceLoader::CallDidFinishLoading, - weak_ptr_factory_.GetWeakPtr())); + if (called_from_resource_controller) { + // Delay self-destruction since we don't know how we were reached. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&ResourceLoader::CallDidFinishLoading, + weak_ptr_factory_.GetWeakPtr())); + } else { + CallDidFinishLoading(); + } break; } } @@ -558,16 +641,13 @@ tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION("475761 OnResponseStarted()")); - bool defer = false; - if (!handler_->OnResponseStarted(response.get(), &defer)) { - Cancel(); - } else if (defer) { - read_deferral_start_time_ = base::TimeTicks::Now(); - deferred_stage_ = DEFERRED_READ; // Read first chunk when resumed. - } + read_deferral_start_time_ = base::TimeTicks::Now(); + ScopedDeferral scoped_deferral(this, DEFERRED_READ); + handler_->OnResponseStarted(response.get(), + base::MakeUnique<Controller>(this)); } -void ResourceLoader::ReadMore(bool is_continuation) { +void ResourceLoader::ReadMore(bool handle_result_async) { TRACE_EVENT_WITH_FLOW0("loading", "ResourceLoader::ReadMore", this, TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); DCHECK(!is_deferred()); @@ -598,7 +678,7 @@ if (result == net::ERR_IO_PENDING) return; - if (!is_continuation || result <= 0) { + if (!handle_result_async || result <= 0) { OnReadCompleted(request_.get(), result); } else { // Else, trigger OnReadCompleted asynchronously to avoid starving the IO @@ -619,7 +699,7 @@ read_deferral_start_time_ = base::TimeTicks(); } if (request_->status().is_success()) { - ReadMore(false); // Read the next chunk (OK to complete synchronously). + ReadMore(false /* handle_result_asynchronously */); } else { ResponseCompleted(); } @@ -636,18 +716,9 @@ tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION("475761 OnReadCompleted()")); - bool defer = false; - if (!handler_->OnReadCompleted(bytes_read, &defer)) { - Cancel(); - } else if (defer) { - deferred_stage_ = - bytes_read > 0 ? DEFERRED_READ : DEFERRED_RESPONSE_COMPLETE; - } - - // Note: the request may still have been cancelled while OnReadCompleted - // returns true if OnReadCompleted caused request to get cancelled - // out-of-band. (In AwResourceDispatcherHostDelegate::DownloadStarting, for - // instance.) + ScopedDeferral scoped_deferral( + this, bytes_read > 0 ? DEFERRED_READ : DEFERRED_RESPONSE_COMPLETE); + handler_->OnReadCompleted(bytes_read, base::MakeUnique<Controller>(this)); } void ResourceLoader::ResponseCompleted() { @@ -657,22 +728,13 @@ DVLOG(1) << "ResponseCompleted: " << request_->url().spec(); RecordHistograms(); - bool defer = false; - { - // TODO(darin): Remove ScopedTracker below once crbug.com/475761 is fixed. - tracked_objects::ScopedTracker tracking_profile( - FROM_HERE_WITH_EXPLICIT_FUNCTION("475761 OnResponseCompleted()")); + // TODO(darin): Remove ScopedTracker below once crbug.com/475761 is fixed. + tracked_objects::ScopedTracker tracking_profile( + FROM_HERE_WITH_EXPLICIT_FUNCTION("475761 OnResponseCompleted()")); - handler_->OnResponseCompleted(request_->status(), &defer); - } - if (defer) { - // The handler is not ready to die yet. We will call DidFinishLoading when - // we resume. - deferred_stage_ = DEFERRED_FINISH; - } else { - // This will result in our destruction. - CallDidFinishLoading(); - } + ScopedDeferral scoped_deferral(this, DEFERRED_FINISH); + handler_->OnResponseCompleted(request_->status(), + base::MakeUnique<Controller>(this)); } void ResourceLoader::CallDidFinishLoading() {
diff --git a/content/browser/loader/resource_loader.h b/content/browser/loader/resource_loader.h index 40603a4..e7da4b7c 100644 --- a/content/browser/loader/resource_loader.h +++ b/content/browser/loader/resource_loader.h
@@ -12,6 +12,7 @@ #include "base/memory/weak_ptr.h" #include "base/time/time.h" #include "content/browser/loader/resource_controller.h" +#include "content/browser/loader/resource_handler.h" #include "content/browser/ssl/ssl_client_auth_handler.h" #include "content/browser/ssl/ssl_error_handler.h" #include "content/common/content_export.h" @@ -34,7 +35,7 @@ class CONTENT_EXPORT ResourceLoader : public net::URLRequest::Delegate, public SSLErrorHandler::Delegate, public SSLClientAuthHandler::Delegate, - public ResourceController { + public ResourceHandler::Delegate { public: ResourceLoader(std::unique_ptr<net::URLRequest> request, std::unique_ptr<ResourceHandler> handler, @@ -53,7 +54,13 @@ void ClearLoginDelegate(); + // ResourceHandler::Delegate implementation: + void OutOfBandCancel(int error_code, bool tell_renderer) override; + private: + // ResourceController implementation for the ResourceLoader. + class Controller; + // net::URLRequest::Delegate implementation: void OnReceivedRedirect(net::URLRequest* request, const net::RedirectInfo& redirect_info, @@ -76,17 +83,25 @@ void ContinueWithCertificate(net::X509Certificate* cert) override; void CancelCertificateSelection() override; - // ResourceController implementation: - void Resume() override; - void Cancel() override; - void CancelAndIgnore() override; - void CancelWithError(int error_code) override; + // These correspond to Controller's methods. + // TODO(mmenke): Seems like this could be simplified a little. + + // |called_from_resource_controller| is true if called directly from a + // ResourceController, in which case |resource_handler_| must not be invoked + // or destroyed synchronously to avoid re-entrancy issues, and false + // otherwise. + void Resume(bool called_from_resource_controller); + void Cancel(); + void CancelAndIgnore(); + void CancelWithError(int error_code); void StartRequestInternal(); void CancelRequestInternal(int error, bool from_renderer); void FollowDeferredRedirectInternal(); void CompleteResponseStarted(); - void ReadMore(bool is_continuation); + // If |handle_result_async| is true, the result of a read that completed + // synchronously will be handled asynchronously, except on EOF or error. + void ReadMore(bool handle_result_async); void ResumeReading(); // Passes a read result to the handler. void CompleteRead(int bytes_read); @@ -110,6 +125,12 @@ enum DeferredStage { DEFERRED_NONE, + // Magic deferral "stage" which means that the code is currently in a + // recursive call from the ResourceLoader. When in this state, Resume() does + // nothing but update the deferral state, and when the stack is unwound back + // up to the ResourceLoader, the request will be continued. This is used to + // prevent the stack from getting too deep. + DEFERRED_SYNC, DEFERRED_START, DEFERRED_REDIRECT, DEFERRED_READ, @@ -118,6 +139,8 @@ }; DeferredStage deferred_stage_; + class ScopedDeferral; + std::unique_ptr<net::URLRequest> request_; std::unique_ptr<ResourceHandler> handler_; ResourceLoaderDelegate* delegate_;
diff --git a/content/browser/loader/resource_loader_unittest.cc b/content/browser/loader/resource_loader_unittest.cc index 9f3d17f..8ef2da03 100644 --- a/content/browser/loader/resource_loader_unittest.cc +++ b/content/browser/loader/resource_loader_unittest.cc
@@ -243,7 +243,8 @@ class MockHTTPSJobURLRequestInterceptor : public net::URLRequestInterceptor { public: - MockHTTPSJobURLRequestInterceptor(bool redirect) : redirect_(redirect) {} + explicit MockHTTPSJobURLRequestInterceptor(bool redirect) + : redirect_(redirect) {} ~MockHTTPSJobURLRequestInterceptor() override {} // net::URLRequestInterceptor: @@ -261,7 +262,6 @@ bool redirect_; }; -// Arbitrary read buffer size. // Test browser client that captures calls to SelectClientCertificates and // records the arguments of the most recent call for later inspection. class SelectCertificateBrowserClient : public TestContentBrowserClient { @@ -1372,7 +1372,9 @@ EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called()); EXPECT_EQ(1, handle_external_protocol_); - raw_ptr_resource_handler_->CancelWithError(net::ERR_FAILED); + // Can't cancel through the ResourceHandler, since that depends on + // ResourceDispatachHost, which these tests don't use. + loader_->CancelRequest(false); raw_ptr_resource_handler_->WaitUntilResponseComplete(); EXPECT_EQ(0, did_received_redirect_); @@ -1383,7 +1385,8 @@ EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called()); EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_started_called()); EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called()); - EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error()); + EXPECT_EQ(net::ERR_ABORTED, + raw_ptr_resource_handler_->final_status().error()); EXPECT_EQ("", raw_ptr_resource_handler_->body()); } @@ -1401,7 +1404,9 @@ EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called()); EXPECT_EQ(1, handle_external_protocol_); - raw_ptr_resource_handler_->CancelWithError(net::ERR_FAILED); + // Can't cancel through the ResourceHandler, since that depends on + // ResourceDispatachHost, which these tests don't use. + loader_->CancelRequest(false); raw_ptr_resource_handler_->WaitUntilResponseComplete(); EXPECT_EQ(0, did_received_redirect_); EXPECT_EQ(1, did_receive_response_); @@ -1412,7 +1417,8 @@ EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called()); EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called()); EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called()); - EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error()); + EXPECT_EQ(net::ERR_ABORTED, + raw_ptr_resource_handler_->final_status().error()); EXPECT_EQ("", raw_ptr_resource_handler_->body()); } @@ -1420,8 +1426,9 @@ raw_ptr_resource_handler_->set_defer_on_will_start(true); loader_->StartRequest(); + raw_ptr_resource_handler_->WaitUntilDeferred(); loader_->CancelRequest(true); - static_cast<ResourceController*>(loader_.get())->Resume(); + raw_ptr_resource_handler_->Resume(); } class EffectiveConnectionTypeResourceLoaderTest : public ResourceLoaderTest {
diff --git a/content/browser/loader/stream_resource_handler.cc b/content/browser/loader/stream_resource_handler.cc index 3560f98..117c193 100644 --- a/content/browser/loader/stream_resource_handler.cc +++ b/content/browser/loader/stream_resource_handler.cc
@@ -4,7 +4,9 @@ #include "content/browser/loader/stream_resource_handler.h" +#include "base/bind.h" #include "base/logging.h" +#include "content/browser/loader/resource_controller.h" #include "net/url_request/url_request_status.h" namespace content { @@ -13,31 +15,32 @@ StreamRegistry* registry, const GURL& origin) : ResourceHandler(request) { - writer_.InitializeStream(registry, origin); + writer_.InitializeStream(registry, origin, + base::Bind(&StreamResourceHandler::OutOfBandCancel, + base::Unretained(this), net::ERR_ABORTED, + true /* tell_renderer */)); } StreamResourceHandler::~StreamResourceHandler() { } -void StreamResourceHandler::SetController(ResourceController* controller) { - writer_.set_controller(controller); - ResourceHandler::SetController(controller); -} - -bool StreamResourceHandler::OnRequestRedirected( +void StreamResourceHandler::OnRequestRedirected( const net::RedirectInfo& redirect_info, ResourceResponse* resp, - bool* defer) { - return true; + std::unique_ptr<ResourceController> controller) { + controller->Resume(); } -bool StreamResourceHandler::OnResponseStarted(ResourceResponse* resp, - bool* defer) { - return true; +void StreamResourceHandler::OnResponseStarted( + ResourceResponse* resp, + std::unique_ptr<ResourceController> controller) { + controller->Resume(); } -bool StreamResourceHandler::OnWillStart(const GURL& url, bool* defer) { - return true; +void StreamResourceHandler::OnWillStart( + const GURL& url, + std::unique_ptr<ResourceController> controller) { + controller->Resume(); } bool StreamResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, @@ -47,15 +50,19 @@ return true; } -bool StreamResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { - writer_.OnReadCompleted(bytes_read, defer); - return true; +void StreamResourceHandler::OnReadCompleted( + int bytes_read, + std::unique_ptr<ResourceController> controller) { + writer_.OnReadCompleted(bytes_read, + base::Bind(&ResourceController::Resume, + base::Passed(std::move(controller)))); } void StreamResourceHandler::OnResponseCompleted( const net::URLRequestStatus& status, - bool* defer) { + std::unique_ptr<ResourceController> controller) { writer_.Finalize(status.error()); + controller->Resume(); } void StreamResourceHandler::OnDataDownloaded(int bytes_downloaded) {
diff --git a/content/browser/loader/stream_resource_handler.h b/content/browser/loader/stream_resource_handler.h index 4f6201f1..719c30bce 100644 --- a/content/browser/loader/stream_resource_handler.h +++ b/content/browser/loader/stream_resource_handler.h
@@ -18,6 +18,7 @@ namespace content { +class ResourceController; class StreamRegistry; // Redirect this resource to a stream. @@ -31,16 +32,18 @@ const GURL& origin); ~StreamResourceHandler() override; - void SetController(ResourceController* controller) override; - // Not needed, as this event handler ought to be the final resource. - bool OnRequestRedirected(const net::RedirectInfo& redirect_info, - ResourceResponse* resp, - bool* defer) override; + void OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* resp, + std::unique_ptr<ResourceController> controller) override; - bool OnResponseStarted(ResourceResponse* resp, bool* defer) override; + void OnResponseStarted( + ResourceResponse* resp, + std::unique_ptr<ResourceController> controller) override; - bool OnWillStart(const GURL& url, bool* defer) override; + void OnWillStart(const GURL& url, + std::unique_ptr<ResourceController> controller) override; // Create a new buffer to store received data. bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, @@ -48,10 +51,12 @@ int min_size) override; // A read was completed, forward the data to the Stream. - bool OnReadCompleted(int bytes_read, bool* defer) override; + void OnReadCompleted(int bytes_read, + std::unique_ptr<ResourceController> controller) override; - void OnResponseCompleted(const net::URLRequestStatus& status, - bool* defer) override; + void OnResponseCompleted( + const net::URLRequestStatus& status, + std::unique_ptr<ResourceController> controller) override; void OnDataDownloaded(int bytes_downloaded) override;
diff --git a/content/browser/loader/stream_writer.cc b/content/browser/loader/stream_writer.cc index 4e815553..3792a17 100644 --- a/content/browser/loader/stream_writer.cc +++ b/content/browser/loader/stream_writer.cc
@@ -4,6 +4,7 @@ #include "content/browser/loader/stream_writer.h" +#include "base/callback_helpers.h" #include "base/guid.h" #include "content/browser/loader/resource_controller.h" #include "content/browser/streams/stream.h" @@ -14,8 +15,7 @@ namespace content { -StreamWriter::StreamWriter() : controller_(nullptr), immediate_mode_(false) { -} +StreamWriter::StreamWriter() : immediate_mode_(false) {} StreamWriter::~StreamWriter() { if (stream_.get()) @@ -23,7 +23,9 @@ } void StreamWriter::InitializeStream(StreamRegistry* registry, - const GURL& origin) { + const GURL& origin, + const base::Closure& cancel_callback) { + cancel_callback_ = cancel_callback; DCHECK(!stream_.get()); // TODO(tyoshino): Find a way to share this with the blob URL creation in @@ -47,9 +49,14 @@ *buf_size = kReadBufSize; } -void StreamWriter::OnReadCompleted(int bytes_read, bool* defer) { - if (!bytes_read) +void StreamWriter::OnReadCompleted( + int bytes_read, + const base::Closure& need_more_data_callback) { + DCHECK(!need_more_data_callback_); + if (!bytes_read) { + need_more_data_callback.Run(); return; + } // We have more data to read. DCHECK(read_buffer_.get()); @@ -63,8 +70,11 @@ if (immediate_mode_) stream_->Flush(); - if (!stream_->can_add_data()) - *defer = true; + if (!stream_->can_add_data()) { + need_more_data_callback_ = need_more_data_callback; + return; + } + need_more_data_callback.Run(); } void StreamWriter::Finalize(int status) { @@ -75,11 +85,11 @@ } void StreamWriter::OnSpaceAvailable(Stream* stream) { - controller_->Resume(); + base::ResetAndReturn(&need_more_data_callback_).Run(); } void StreamWriter::OnClose(Stream* stream) { - controller_->Cancel(); + cancel_callback_.Run(); } } // namespace content
diff --git a/content/browser/loader/stream_writer.h b/content/browser/loader/stream_writer.h index 64ce43e..f6d974d 100644 --- a/content/browser/loader/stream_writer.h +++ b/content/browser/loader/stream_writer.h
@@ -5,6 +5,7 @@ #ifndef CONTENT_BROWSER_LOADER_STREAM_WRITER_H_ #define CONTENT_BROWSER_LOADER_STREAM_WRITER_H_ +#include "base/callback.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "content/browser/streams/stream_write_observer.h" @@ -17,7 +18,6 @@ namespace content { -class ResourceController; class Stream; class StreamRegistry; @@ -33,10 +33,6 @@ Stream* stream() { return stream_.get(); } - void set_controller(ResourceController* controller) { - controller_ = controller; - } - // When immediate mode is enabled, the |stream_| is flushed every time new // data is made available by calls to OnReadCompleted. void set_immediate_mode(bool enabled) { immediate_mode_ = enabled; } @@ -44,9 +40,11 @@ // Initializes the writer with a new Stream in |registry|. |origin| will be // used to construct the URL for the Stream. See WebCore::BlobURL and and // WebCore::SecurityOrigin in Blink to understand how origin check is done on - // resource loading. + // resource loading. |cancel_callback| must be called if the StreamWriter + // closes the stream. void InitializeStream(StreamRegistry* registry, - const GURL& origin); + const GURL& origin, + const base::Closure& cancel_callback); // Prepares a buffer to read data from the request. This call will be followed // by either OnReadCompleted (on successful read or EOF) or destruction. The @@ -60,12 +58,14 @@ int* buf_size, int min_size); - // A read was completed, forward the data to the Stream. If |*defer| is set to - // true, the implementation must not continue to process the request until - // Resume is called on |controller_|. + // A read was completed, forward the data to the Stream. + // |need_more_data_callback| must be called (synchronously or asynchronously) + // once the writer is ready for more data. Invoking the callback may result + // in more data being received recursively. // // InitializeStream must have been called before calling OnReadCompleted. - void OnReadCompleted(int bytes_read, bool* defer); + void OnReadCompleted(int bytes_read, + const base::Closure& need_more_data_callback); // Called when there is no more data to read to the stream. void Finalize(int status); @@ -75,11 +75,13 @@ void OnSpaceAvailable(Stream* stream) override; void OnClose(Stream* stream) override; - ResourceController* controller_; scoped_refptr<Stream> stream_; scoped_refptr<net::IOBuffer> read_buffer_; bool immediate_mode_; + base::Closure cancel_callback_; + base::Closure need_more_data_callback_; + DISALLOW_COPY_AND_ASSIGN(StreamWriter); };
diff --git a/content/browser/loader/sync_resource_handler.cc b/content/browser/loader/sync_resource_handler.cc index 21e520b..3dbea2d 100644 --- a/content/browser/loader/sync_resource_handler.cc +++ b/content/browser/loader/sync_resource_handler.cc
@@ -7,6 +7,7 @@ #include "base/callback_helpers.h" #include "base/logging.h" #include "content/browser/loader/netlog_observer.h" +#include "content/browser/loader/resource_controller.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/loader/resource_request_info_impl.h" #include "content/common/resource_messages.h" @@ -35,10 +36,10 @@ result_handler_.Run(nullptr); } -bool SyncResourceHandler::OnRequestRedirected( +void SyncResourceHandler::OnRequestRedirected( const net::RedirectInfo& redirect_info, ResourceResponse* response, - bool* defer) { + std::unique_ptr<ResourceController> controller) { if (rdh_->delegate()) { rdh_->delegate()->OnRequestRedirected( redirect_info.new_url, request(), GetRequestInfo()->GetContext(), @@ -51,21 +52,24 @@ // WebCore/platform/network/cf/ResourceHandleCFNet.cpp :-( if (redirect_info.new_url.GetOrigin() != result_.final_url.GetOrigin()) { LOG(ERROR) << "Cross origin redirect denied"; - return false; + controller->Cancel(); + return; } result_.final_url = redirect_info.new_url; total_transfer_size_ += request()->GetTotalReceivedBytes(); - return true; + controller->Resume(); } -bool SyncResourceHandler::OnResponseStarted( +void SyncResourceHandler::OnResponseStarted( ResourceResponse* response, - bool* defer) { + std::unique_ptr<ResourceController> controller) { ResourceRequestInfoImpl* info = GetRequestInfo(); DCHECK(info->requester_info()->IsRenderer()); - if (!info->requester_info()->filter()) - return false; + if (!info->requester_info()->filter()) { + controller->Cancel(); + return; + } if (rdh_->delegate()) { rdh_->delegate()->OnResponseStarted(request(), info->GetContext(), @@ -83,11 +87,13 @@ result_.response_time = response->head.response_time; result_.load_timing = response->head.load_timing; result_.devtools_info = response->head.devtools_info; - return true; + controller->Resume(); } -bool SyncResourceHandler::OnWillStart(const GURL& url, bool* defer) { - return true; +void SyncResourceHandler::OnWillStart( + const GURL& url, + std::unique_ptr<ResourceController> controller) { + controller->Resume(); } bool SyncResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, @@ -99,16 +105,17 @@ return true; } -bool SyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { - if (!bytes_read) - return true; - result_.data.append(read_buffer_->data(), bytes_read); - return true; +void SyncResourceHandler::OnReadCompleted( + int bytes_read, + std::unique_ptr<ResourceController> controller) { + if (bytes_read) + result_.data.append(read_buffer_->data(), bytes_read); + controller->Resume(); } void SyncResourceHandler::OnResponseCompleted( const net::URLRequestStatus& status, - bool* defer) { + std::unique_ptr<ResourceController> controller) { result_.error_code = status.error(); int total_transfer_size = request()->GetTotalReceivedBytes(); @@ -116,6 +123,8 @@ result_.encoded_body_length = request()->GetRawBodyBytes(); base::ResetAndReturn(&result_handler_).Run(&result_); + + controller->Resume(); } void SyncResourceHandler::OnDataDownloaded(int bytes_downloaded) {
diff --git a/content/browser/loader/sync_resource_handler.h b/content/browser/loader/sync_resource_handler.h index 25f79678..c9ece9d 100644 --- a/content/browser/loader/sync_resource_handler.h +++ b/content/browser/loader/sync_resource_handler.h
@@ -19,6 +19,7 @@ } namespace content { +class ResourceController; // Used to complete a synchronous resource request in response to resource load // events from the resource dispatcher host. @@ -32,17 +33,23 @@ ResourceDispatcherHostImpl* resource_dispatcher_host); ~SyncResourceHandler() override; - bool OnRequestRedirected(const net::RedirectInfo& redirect_info, - ResourceResponse* response, - bool* defer) override; - bool OnResponseStarted(ResourceResponse* response, bool* defer) override; - bool OnWillStart(const GURL& url, bool* defer) override; + void OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; + void OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; + void OnWillStart(const GURL& url, + std::unique_ptr<ResourceController> controller) override; bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, int* buf_size, int min_size) override; - bool OnReadCompleted(int bytes_read, bool* defer) override; - void OnResponseCompleted(const net::URLRequestStatus& status, - bool* defer) override; + void OnReadCompleted(int bytes_read, + std::unique_ptr<ResourceController> controller) override; + void OnResponseCompleted( + const net::URLRequestStatus& status, + std::unique_ptr<ResourceController> controller) override; void OnDataDownloaded(int bytes_downloaded) override; private:
diff --git a/content/browser/loader/test_resource_handler.cc b/content/browser/loader/test_resource_handler.cc index a6c8072..a4e0816 100644 --- a/content/browser/loader/test_resource_handler.cc +++ b/content/browser/loader/test_resource_handler.cc
@@ -5,6 +5,7 @@ #include "content/browser/loader/test_resource_handler.h" #include "base/logging.h" +#include "base/memory/ptr_util.h" #include "content/browser/loader/resource_controller.h" #include "content/public/common/resource_response.h" #include "testing/gtest/include/gtest/gtest.h" @@ -48,14 +49,10 @@ TestResourceHandler::~TestResourceHandler() {} -void TestResourceHandler::SetController(ResourceController* controller) { - controller_ = controller; -} - -bool TestResourceHandler::OnRequestRedirected( +void TestResourceHandler::OnRequestRedirected( const net::RedirectInfo& redirect_info, ResourceResponse* response, - bool* defer) { + std::unique_ptr<ResourceController> controller) { EXPECT_FALSE(canceled_); EXPECT_EQ(1, on_will_start_called_); EXPECT_EQ(0, on_response_started_called_); @@ -66,18 +63,23 @@ if (!on_request_redirected_result_) { canceled_ = true; - return false; + controller->Cancel(); + return; } - *defer = defer_on_request_redirected_; - defer_on_request_redirected_ = false; - if (*defer) + if (defer_on_request_redirected_) { + defer_on_request_redirected_ = false; + HoldController(std::move(controller)); deferred_run_loop_->Quit(); - return true; + return; + } + + controller->Resume(); } -bool TestResourceHandler::OnResponseStarted(ResourceResponse* response, - bool* defer) { +void TestResourceHandler::OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) { EXPECT_FALSE(canceled_); EXPECT_EQ(1, on_will_start_called_); EXPECT_EQ(0, on_response_started_called_); @@ -91,17 +93,28 @@ if (!on_response_started_result_) { canceled_ = true; - return false; + controller->Cancel(); + return; } - *defer = defer_on_response_started_; - defer_on_response_started_ = false; - if (*defer) + if (!on_request_redirected_result_) { + controller->Cancel(); + return; + } + + if (defer_on_response_started_) { + defer_on_response_started_ = false; + HoldController(std::move(controller)); deferred_run_loop_->Quit(); - return true; + return; + } + + controller->Resume(); } -bool TestResourceHandler::OnWillStart(const GURL& url, bool* defer) { +void TestResourceHandler::OnWillStart( + const GURL& url, + std::unique_ptr<ResourceController> controller) { EXPECT_FALSE(canceled_); EXPECT_EQ(0, on_response_started_called_); EXPECT_EQ(0, on_will_start_called_); @@ -114,13 +127,18 @@ if (!on_will_start_result_) { canceled_ = true; - return false; + controller->Cancel(); + return; } - *defer = defer_on_will_start_; - if (*defer) + if (defer_on_will_start_) { + defer_on_will_start_ = false; + HoldController(std::move(controller)); deferred_run_loop_->Quit(); - return true; + return; + } + + controller->Resume(); } bool TestResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, @@ -129,7 +147,12 @@ EXPECT_FALSE(canceled_); EXPECT_FALSE(expect_on_data_downloaded_); EXPECT_EQ(0, on_response_completed_called_); - ScopedCallDepthTracker call_depth_tracker(&call_depth_); + // Only create a ScopedCallDepthTracker if not called re-entrantly, as + // OnWillRead may be called synchronously in response to a Resume(), but + // nothing may be called synchronously in response to the OnWillRead call. + std::unique_ptr<ScopedCallDepthTracker> call_depth_tracker; + if (call_depth_ == 0) + call_depth_tracker = base::MakeUnique<ScopedCallDepthTracker>(&call_depth_); ++on_will_read_called_; @@ -144,7 +167,9 @@ return on_will_read_result_; } -bool TestResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { +void TestResourceHandler::OnReadCompleted( + int bytes_read, + std::unique_ptr<ResourceController> controller) { EXPECT_FALSE(canceled_); EXPECT_FALSE(expect_on_data_downloaded_); EXPECT_EQ(1, on_will_start_called_); @@ -164,23 +189,23 @@ if (!on_read_completed_result_ || (!on_read_eof_result_ && bytes_read == 0)) { canceled_ = true; - return false; + controller->Cancel(); + return; } - *defer = defer_on_read_completed_; - defer_on_read_completed_ = false; - if (bytes_read == 0 && defer_on_read_eof_) - *defer = true; - - if (*defer) + if (defer_on_read_completed_ || (bytes_read == 0 && defer_on_read_eof_)) { + defer_on_read_completed_ = false; + HoldController(std::move(controller)); deferred_run_loop_->Quit(); + return; + } - return true; + controller->Resume(); } void TestResourceHandler::OnResponseCompleted( const net::URLRequestStatus& status, - bool* defer) { + std::unique_ptr<ResourceController> controller) { ScopedCallDepthTracker call_depth_tracker(&call_depth_); EXPECT_EQ(0, on_response_completed_called_); @@ -192,12 +217,19 @@ if (request_status_ptr_) *request_status_ptr_ = status; final_status_ = status; - *defer = defer_on_response_completed_; - defer_on_response_completed_ = false; - if (*defer) - deferred_run_loop_->Quit(); + // Consider response completed. Even if deferring, the TestResourceHandler + // won't be called again. response_complete_run_loop_.Quit(); + + if (defer_on_response_completed_) { + defer_on_response_completed_ = false; + HoldController(std::move(controller)); + deferred_run_loop_->Quit(); + return; + } + + controller->Resume(); } void TestResourceHandler::OnDataDownloaded(int bytes_downloaded) { @@ -211,13 +243,13 @@ void TestResourceHandler::Resume() { ScopedCallDepthTracker call_depth_tracker(&call_depth_); - controller_->Resume(); + ResourceHandler::Resume(); } void TestResourceHandler::CancelWithError(net::Error net_error) { ScopedCallDepthTracker call_depth_tracker(&call_depth_); canceled_ = true; - controller_->CancelWithError(net_error); + ResourceHandler::CancelWithError(net_error); } void TestResourceHandler::SetBufferSize(int buffer_size) {
diff --git a/content/browser/loader/test_resource_handler.h b/content/browser/loader/test_resource_handler.h index 23316553..a65c70d 100644 --- a/content/browser/loader/test_resource_handler.h +++ b/content/browser/loader/test_resource_handler.h
@@ -43,24 +43,27 @@ ~TestResourceHandler() override; // ResourceHandler implementation: - void SetController(ResourceController* controller) override; - bool OnRequestRedirected(const net::RedirectInfo& redirect_info, - ResourceResponse* response, - bool* defer) override; - bool OnResponseStarted(ResourceResponse* response, bool* defer) override; - bool OnWillStart(const GURL& url, bool* defer) override; + void OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; + void OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; + void OnWillStart(const GURL& url, + std::unique_ptr<ResourceController> controller) override; bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, int* buf_size, int min_size) override; - bool OnReadCompleted(int bytes_read, bool* defer) override; - void OnResponseCompleted(const net::URLRequestStatus& status, - bool* defer) override; + void OnReadCompleted(int bytes_read, + std::unique_ptr<ResourceController> controller) override; + void OnResponseCompleted( + const net::URLRequestStatus& status, + std::unique_ptr<ResourceController> controller) override; void OnDataDownloaded(int bytes_downloaded) override; - // Invoke the corresponding methods on the ResourceHandler's - // ResourceController. void Resume(); - void CancelWithError(net::Error net_error); + void CancelWithError(net::Error error_code); // Sets the size of the read buffer returned by OnWillRead. Releases reference // to previous read buffer. Default size is 2048 bytes. @@ -169,8 +172,6 @@ scoped_refptr<net::IOBuffer> buffer_; size_t buffer_size_; - ResourceController* controller_; - bool on_will_start_result_ = true; bool on_request_redirected_result_ = true; bool on_response_started_result_ = true;
diff --git a/content/browser/loader/throttling_resource_handler.cc b/content/browser/loader/throttling_resource_handler.cc index 0c1ed65..a106f05 100644 --- a/content/browser/loader/throttling_resource_handler.cc +++ b/content/browser/loader/throttling_resource_handler.cc
@@ -32,93 +32,116 @@ ThrottlingResourceHandler::~ThrottlingResourceHandler() { } -bool ThrottlingResourceHandler::OnRequestRedirected( +void ThrottlingResourceHandler::OnRequestRedirected( const net::RedirectInfo& redirect_info, ResourceResponse* response, - bool* defer) { + std::unique_ptr<ResourceController> controller) { + DCHECK(!has_controller()); DCHECK(!cancelled_by_resource_throttle_); - DCHECK(!*defer); + HoldController(std::move(controller)); while (next_index_ < throttles_.size()) { int index = next_index_; - throttles_[index]->WillRedirectRequest(redirect_info, defer); + bool defer = false; + throttles_[index]->WillRedirectRequest(redirect_info, &defer); next_index_++; if (cancelled_by_resource_throttle_) - return false; - if (*defer) { + return; + + if (defer) { OnRequestDeferred(index); deferred_stage_ = DEFERRED_REDIRECT; deferred_redirect_ = redirect_info; deferred_response_ = response; - return true; // Do not cancel. + return; } } next_index_ = 0; // Reset for next time. - return next_handler_->OnRequestRedirected(redirect_info, response, defer); + next_handler_->OnRequestRedirected(redirect_info, response, + ReleaseController()); } -bool ThrottlingResourceHandler::OnWillStart(const GURL& url, bool* defer) { +void ThrottlingResourceHandler::OnWillStart( + const GURL& url, + std::unique_ptr<ResourceController> controller) { DCHECK(!cancelled_by_resource_throttle_); - DCHECK(!*defer); + DCHECK(!has_controller()); + HoldController(std::move(controller)); while (next_index_ < throttles_.size()) { int index = next_index_; - throttles_[index]->WillStartRequest(defer); + bool defer = false; + throttles_[index]->WillStartRequest(&defer); next_index_++; if (cancelled_by_resource_throttle_) - return false; - if (*defer) { + return; + if (defer) { OnRequestDeferred(index); deferred_stage_ = DEFERRED_START; deferred_url_ = url; - return true; // Do not cancel. + return; } } next_index_ = 0; // Reset for next time. - return next_handler_->OnWillStart(url, defer); + return next_handler_->OnWillStart(url, ReleaseController()); } -bool ThrottlingResourceHandler::OnResponseStarted(ResourceResponse* response, - bool* defer) { +void ThrottlingResourceHandler::OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) { DCHECK(!cancelled_by_resource_throttle_); - DCHECK(!*defer); + DCHECK(!has_controller()); + HoldController(std::move(controller)); while (next_index_ < throttles_.size()) { int index = next_index_; - throttles_[index]->WillProcessResponse(defer); + bool defer = false; + throttles_[index]->WillProcessResponse(&defer); next_index_++; if (cancelled_by_resource_throttle_) - return false; - if (*defer) { + return; + if (defer) { OnRequestDeferred(index); deferred_stage_ = DEFERRED_RESPONSE; deferred_response_ = response; - return true; // Do not cancel. + return; } } next_index_ = 0; // Reset for next time. - return next_handler_->OnResponseStarted(response, defer); + return next_handler_->OnResponseStarted(response, ReleaseController()); } void ThrottlingResourceHandler::Cancel() { + if (!has_controller()) { + OutOfBandCancel(net::ERR_ABORTED, false /* tell_renderer */); + return; + } cancelled_by_resource_throttle_ = true; - controller()->Cancel(); + ResourceHandler::Cancel(); } void ThrottlingResourceHandler::CancelAndIgnore() { + if (!has_controller()) { + OutOfBandCancel(net::ERR_ABORTED, false /* tell_renderer */); + return; + } cancelled_by_resource_throttle_ = true; - controller()->CancelAndIgnore(); + ResourceHandler::CancelAndIgnore(); } void ThrottlingResourceHandler::CancelWithError(int error_code) { + if (!has_controller()) { + OutOfBandCancel(error_code, false /* tell_renderer */); + return; + } cancelled_by_resource_throttle_ = true; - controller()->CancelWithError(error_code); + ResourceHandler::CancelWithError(error_code); } void ThrottlingResourceHandler::Resume() { @@ -129,6 +152,8 @@ if (cancelled_by_resource_throttle_) return; + DCHECK(has_controller()); + DeferredStage last_deferred_stage = deferred_stage_; deferred_stage_ = DEFERRED_NONE; // Clear information about the throttle that delayed the request. @@ -151,46 +176,34 @@ void ThrottlingResourceHandler::ResumeStart() { DCHECK(!cancelled_by_resource_throttle_); + DCHECK(has_controller()); GURL url = deferred_url_; deferred_url_ = GURL(); - bool defer = false; - if (!OnWillStart(url, &defer)) { - controller()->Cancel(); - } else if (!defer) { - controller()->Resume(); - } + OnWillStart(url, ReleaseController()); } void ThrottlingResourceHandler::ResumeRedirect() { DCHECK(!cancelled_by_resource_throttle_); + DCHECK(has_controller()); net::RedirectInfo redirect_info = deferred_redirect_; deferred_redirect_ = net::RedirectInfo(); scoped_refptr<ResourceResponse> response; deferred_response_.swap(response); - bool defer = false; - if (!OnRequestRedirected(redirect_info, response.get(), &defer)) { - controller()->Cancel(); - } else if (!defer) { - controller()->Resume(); - } + OnRequestRedirected(redirect_info, response.get(), ReleaseController()); } void ThrottlingResourceHandler::ResumeResponse() { DCHECK(!cancelled_by_resource_throttle_); + DCHECK(has_controller()); scoped_refptr<ResourceResponse> response; deferred_response_.swap(response); - bool defer = false; - if (!OnResponseStarted(response.get(), &defer)) { - controller()->Cancel(); - } else if (!defer) { - controller()->Resume(); - } + OnResponseStarted(response.get(), ReleaseController()); } void ThrottlingResourceHandler::OnRequestDeferred(int throttle_index) {
diff --git a/content/browser/loader/throttling_resource_handler.h b/content/browser/loader/throttling_resource_handler.h index e608f84a..c089979a 100644 --- a/content/browser/loader/throttling_resource_handler.h +++ b/content/browser/loader/throttling_resource_handler.h
@@ -38,11 +38,15 @@ ~ThrottlingResourceHandler() override; // LayeredResourceHandler overrides: - bool OnRequestRedirected(const net::RedirectInfo& redirect_info, - ResourceResponse* response, - bool* defer) override; - bool OnResponseStarted(ResourceResponse* response, bool* defer) override; - bool OnWillStart(const GURL& url, bool* defer) override; + void OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; + void OnResponseStarted( + ResourceResponse* response, + std::unique_ptr<ResourceController> controller) override; + void OnWillStart(const GURL& url, + std::unique_ptr<ResourceController> controller) override; // ResourceThrottle::Delegate implementation: void Cancel() override;
diff --git a/content/browser/quota/OWNERS b/content/browser/quota/OWNERS index 0cf6ed3..c254a8f 100644 --- a/content/browser/quota/OWNERS +++ b/content/browser/quota/OWNERS
@@ -1,2 +1,5 @@ kinuko@chromium.org michaeln@chromium.org + +# TEAM: storage-dev@chromium.org +# COMPONENT: Blink>Storage>Quota
diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc index 9902d94..5f2136b 100644 --- a/content/browser/renderer_host/media/video_capture_controller.cc +++ b/content/browser/renderer_host/media/video_capture_controller.cc
@@ -145,6 +145,11 @@ return consumer_hold_count_ == 0; } +void VideoCaptureController::BufferState::SetFrameFeedbackId(int id) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + frame_feedback_id_ = id; +} + void VideoCaptureController::BufferState::SetConsumerFeedbackObserver( media::VideoFrameConsumerFeedbackObserver* consumer_feedback_observer) { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -377,15 +382,16 @@ DCHECK_NE(buffer_id, media::VideoCaptureBufferPool::kInvalidId); // Insert if not exists. - const auto it = - buffer_id_to_state_map_ - .insert(std::make_pair( - buffer_id, BufferState(buffer_id, buffer.frame_feedback_id(), - consumer_feedback_observer_.get(), - frame_buffer_pool_.get()))) - .first; - BufferState& buffer_state = it->second; + const auto insert_result = buffer_id_to_state_map_.insert(std::make_pair( + buffer_id, BufferState(buffer_id, buffer.frame_feedback_id(), + consumer_feedback_observer_.get(), + frame_buffer_pool_.get()))); + BufferState& buffer_state = insert_result.first->second; DCHECK(buffer_state.HasZeroConsumerHoldCount()); + // If a BufferState for |buffer_id| already existed, we must update the + // |frame_feedback_id| of the existing entry. + if (!insert_result.second) + buffer_state.SetFrameFeedbackId(buffer.frame_feedback_id()); if (state_ == VIDEO_CAPTURE_STATE_STARTED) { if (!frame->metadata()->HasKey(VideoFrameMetadata::FRAME_RATE)) {
diff --git a/content/browser/renderer_host/media/video_capture_controller.h b/content/browser/renderer_host/media/video_capture_controller.h index 76e59cb..8eaa31a 100644 --- a/content/browser/renderer_host/media/video_capture_controller.h +++ b/content/browser/renderer_host/media/video_capture_controller.h
@@ -153,13 +153,14 @@ void IncreaseConsumerCount(); void DecreaseConsumerCount(); bool HasZeroConsumerHoldCount(); + void SetFrameFeedbackId(int id); void SetConsumerFeedbackObserver( media::VideoFrameConsumerFeedbackObserver* consumer_feedback_observer); void SetFrameBufferPool(media::FrameBufferPool* frame_buffer_pool); private: const int buffer_id_; - const int frame_feedback_id_; + int frame_feedback_id_; media::VideoFrameConsumerFeedbackObserver* consumer_feedback_observer_; media::FrameBufferPool* frame_buffer_pool_; double max_consumer_utilization_;
diff --git a/content/browser/renderer_host/media/video_capture_controller_unittest.cc b/content/browser/renderer_host/media/video_capture_controller_unittest.cc index 3561859..18d6b94e 100644 --- a/content/browser/renderer_host/media/video_capture_controller_unittest.cc +++ b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
@@ -593,4 +593,62 @@ Mock::VerifyAndClearExpectations(client_b_.get()); } +// Tests that frame feedback provided by consumers is correctly reported back +// to the producing device for a sequence of frames that is longer than the +// number of buffers shared between the device and consumer. +TEST_F(VideoCaptureControllerTest, FrameFeedbackIsReportedForSequenceOfFrames) { + const int kTestFrameSequenceLength = 10; + media::VideoCaptureFormat arbitrary_format( + gfx::Size(320, 240), arbitrary_frame_rate_, media::PIXEL_FORMAT_I420); + + // Register |client_a_| at |controller_|. + media::VideoCaptureParams session_100; + session_100.requested_format = arbitrary_format; + const VideoCaptureControllerID route_id(0x99); + controller_->AddClient(route_id, client_a_.get(), 100, session_100); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + + for (int frame_index = 0; frame_index < kTestFrameSequenceLength; + frame_index++) { + const int stub_frame_feedback_id = frame_index; + const float stub_consumer_utilization = + static_cast<float>(frame_index) / kTestFrameSequenceLength; + + client_a_->resource_utilization_ = stub_consumer_utilization; + + EXPECT_CALL(*client_a_, + DoBufferReady(route_id, arbitrary_format.frame_size)) + .Times(1); + EXPECT_CALL( + *mock_consumer_feedback_observer_, + OnUtilizationReport(stub_frame_feedback_id, stub_consumer_utilization)) + .Times(1); + + // Device prepares and pushes a frame. + // For the first half of the frames we exercise ReserveOutputBuffer() while + // for the second half we exercise ResurrectLastOutputBuffer(). + // The frame is expected to arrive at |client_a_|.DoBufferReady(), which + // automatically notifies |controller_| that it has finished consuming it. + media::VideoCaptureDevice::Client::Buffer buffer; + if (frame_index < kTestFrameSequenceLength / 2) { + buffer = device_client_->ReserveOutputBuffer( + arbitrary_format.frame_size, arbitrary_format.pixel_format, + arbitrary_format.pixel_storage, stub_frame_feedback_id); + } else { + buffer = device_client_->ResurrectLastOutputBuffer( + arbitrary_format.frame_size, arbitrary_format.pixel_format, + arbitrary_format.pixel_storage, stub_frame_feedback_id); + } + ASSERT_TRUE(buffer.is_valid()); + device_client_->OnIncomingCapturedBuffer( + std::move(buffer), arbitrary_format, arbitrary_reference_time_, + arbitrary_timestamp_); + + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + Mock::VerifyAndClearExpectations(mock_consumer_feedback_observer_); + } +} + } // namespace content
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 9729ee9..7a2b7c2 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1788,6 +1788,7 @@ switches::kMSEVideoBufferSizeLimit, switches::kNoReferrers, switches::kNoSandbox, + switches::kNoUseMusInRenderer, switches::kOverridePluginPowerSaverForTesting, switches::kPassiveListenersDefault, switches::kPpapiInProcess, @@ -1876,7 +1877,6 @@ switches::kIpcDumpDirectory, switches::kIpcFuzzerTestcase, #endif - switches::kUseMusInRenderer, }; renderer_cmd->CopySwitchesFrom(browser_cmd, kSwitchNames, arraysize(kSwitchNames));
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc index c637c02..e16766a 100644 --- a/content/browser/renderer_host/render_view_host_impl.cc +++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -544,6 +544,13 @@ "max_keyframe_distance_ms", base::TimeDelta::FromSeconds(10).InMilliseconds())); + // TODO(servolk, asvitkine): Query the value directly when it is available in + // the renderer process. See https://crbug.com/681160. + prefs.enable_instant_source_buffer_gc = + variations::GetVariationParamByFeatureAsBool( + media::kMemoryPressureBasedSourceBufferGC, + "enable_instant_source_buffer_gc", false); + std::map<std::string, std::string> expensive_background_throttling_prefs; variations::GetVariationParamsByFeature( features::kExpensiveBackgroundTimerThrottling,
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc index da12730..68311d6 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -368,8 +368,8 @@ bool IsMus() { return aura::Env::GetInstance()->mode() == aura::Env::Mode::MUS && - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kUseMusInRenderer); + !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kNoUseMusInRenderer); } ////////////////////////////////////////////////////////////////////////////////
diff --git a/content/public/android/java/src/org/chromium/content/app/ChildProcessServiceImpl.java b/content/public/android/java/src/org/chromium/content/app/ChildProcessServiceImpl.java index 0deff1f..d6f2739 100644 --- a/content/public/android/java/src/org/chromium/content/app/ChildProcessServiceImpl.java +++ b/content/public/android/java/src/org/chromium/content/app/ChildProcessServiceImpl.java
@@ -19,6 +19,7 @@ import org.chromium.base.BaseSwitches; import org.chromium.base.CommandLine; import org.chromium.base.ContextUtils; +import org.chromium.base.JNIUtils; import org.chromium.base.Log; import org.chromium.base.UnguessableToken; import org.chromium.base.annotations.CalledByNative; @@ -31,6 +32,7 @@ import org.chromium.base.library_loader.ProcessInitException; import org.chromium.content.browser.ChildProcessConstants; import org.chromium.content.browser.ChildProcessCreationParams; +import org.chromium.content.common.ContentSwitches; import org.chromium.content.common.FileDescriptorInfo; import org.chromium.content.common.IChildProcessCallback; import org.chromium.content.common.IChildProcessService; @@ -192,6 +194,12 @@ } CommandLine.init(mCommandLineParams); + if (ContentSwitches.SWITCH_RENDERER_PROCESS.equals( + CommandLine.getInstance().getSwitchValue( + ContentSwitches.SWITCH_PROCESS_TYPE))) { + JNIUtils.enableSelectiveJniRegistration(); + } + Linker linker = null; boolean requestedSharedRelro = false; if (Linker.isUsed()) {
diff --git a/content/public/common/common_param_traits_macros.h b/content/public/common/common_param_traits_macros.h index 88d952cc..6098aaa 100644 --- a/content/public/common/common_param_traits_macros.h +++ b/content/public/common/common_param_traits_macros.h
@@ -247,6 +247,7 @@ IPC_STRUCT_TRAITS_MEMBER(hide_download_ui) IPC_STRUCT_TRAITS_MEMBER(background_video_track_optimization_enabled) IPC_STRUCT_TRAITS_MEMBER(max_keyframe_distance_to_disable_background_video) + IPC_STRUCT_TRAITS_MEMBER(enable_instant_source_buffer_gc) IPC_STRUCT_TRAITS_MEMBER(presentation_receiver) IPC_STRUCT_TRAITS_END()
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc index 69e3577..6d1f4a3 100644 --- a/content/public/common/content_switches.cc +++ b/content/public/common/content_switches.cc
@@ -675,6 +675,9 @@ // Disables the sandbox for all process types that are normally sandboxed. const char kNoSandbox[] = "no-sandbox"; +// Do not use the mojo UI Service in the Chrome render process. +const char kNoUseMusInRenderer[] = "no-use-mus-in-renderer"; + // Disables the use of a zygote process for forking child processes. Instead, // child processes will be forked and exec'd directly. Note that --no-sandbox // should also be used together with this flag because the sandbox needs the @@ -883,9 +886,6 @@ // streams (e.g. WebRTC). Works with --use-fake-device-for-media-stream. const char kUseFakeUIForMediaStream[] = "use-fake-ui-for-media-stream"; -// Use the Mandoline UI Service in the Chrome render process. -const char kUseMusInRenderer[] = "use-mus-in-renderer"; - // Texture target for CHROMIUM_image backed content textures. const char kContentImageTextureTarget[] = "content-image-texture-target";
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h index e2d66533..2058361 100644 --- a/content/public/common/content_switches.h +++ b/content/public/common/content_switches.h
@@ -201,6 +201,7 @@ CONTENT_EXPORT extern const char kMuteAudio[]; CONTENT_EXPORT extern const char kNoReferrers[]; CONTENT_EXPORT extern const char kNoSandbox[]; +CONTENT_EXPORT extern const char kNoUseMusInRenderer[]; CONTENT_EXPORT extern const char kNoZygote[]; CONTENT_EXPORT extern const char kEnableAppContainer[]; CONTENT_EXPORT extern const char kDisableAppContainer[]; @@ -247,7 +248,6 @@ CONTENT_EXPORT extern const char kTouchTextSelectionStrategy[]; CONTENT_EXPORT extern const char kUIPrioritizeInGpuProcess[]; CONTENT_EXPORT extern const char kUseFakeUIForMediaStream[]; -CONTENT_EXPORT extern const char kUseMusInRenderer[]; CONTENT_EXPORT extern const char kContentImageTextureTarget[]; CONTENT_EXPORT extern const char kVideoImageTextureTarget[]; CONTENT_EXPORT extern const char kUseMobileUserAgent[];
diff --git a/content/public/common/web_preferences.cc b/content/public/common/web_preferences.cc index c3f26ae..ec26b95 100644 --- a/content/public/common/web_preferences.cc +++ b/content/public/common/web_preferences.cc
@@ -225,6 +225,7 @@ background_video_track_optimization_enabled(false), max_keyframe_distance_to_disable_background_video( base::TimeDelta::FromSeconds(10)), + enable_instant_source_buffer_gc(false), presentation_receiver(false) { standard_font_family_map[kCommonScript] = base::ASCIIToUTF16("Times New Roman");
diff --git a/content/public/common/web_preferences.h b/content/public/common/web_preferences.h index 9bcda9e..3778307 100644 --- a/content/public/common/web_preferences.h +++ b/content/public/common/web_preferences.h
@@ -272,6 +272,14 @@ // the renderer process. See https://crbug.com/681160. base::TimeDelta max_keyframe_distance_to_disable_background_video; + // When memory pressure based garbage collection is enabled for MSE, the + // |enable_instant_source_buffer_gc| flag controls whether the GC is done + // immediately on memory pressure notification or during the next SourceBuffer + // append (slower, but is MSE-spec compliant). + // TODO(servolk, asvitkine): Query the value directly when it is available in + // the renderer process. See https://crbug.com/681160. + bool enable_instant_source_buffer_gc; + // Whether it is a presentation receiver. bool presentation_receiver;
diff --git a/content/renderer/accessibility/blink_ax_tree_source.cc b/content/renderer/accessibility/blink_ax_tree_source.cc index e6c45067..28d3f604 100644 --- a/content/renderer/accessibility/blink_ax_tree_source.cc +++ b/content/renderer/accessibility/blink_ax_tree_source.cc
@@ -83,7 +83,17 @@ void addStringAttribute(blink::WebAXStringAttribute attribute, const blink::WebString& value) override { - NOTREACHED(); + switch (attribute) { + case blink::WebAXStringAttribute::AriaKeyShortcuts: + // TODO(dmazzoni): implement aria-keyshortcuts. http://crbug.com/644766 + break; + case blink::WebAXStringAttribute::AriaRoleDescription: + // TODO(dmazzoni): implement aria-roledescription. + // http://crbug.com/644766 + break; + default: + NOTREACHED(); + } } void addObjectAttribute(blink::WebAXObjectAttribute attribute, @@ -92,6 +102,10 @@ case blink::WebAXObjectAttribute::AriaActiveDescendant: dst_->AddIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID, value.axID()); break; + case blink::WebAXObjectAttribute::AriaErrorMessage: + // TODO(dmazzoni): implement aria-errormessage. + // http://crbug.com/644766 + break; default: NOTREACHED(); } @@ -105,6 +119,9 @@ AddIntListAttributeFromWebObjects(ui::AX_ATTR_CONTROLS_IDS, value, dst_); break; + case blink::WebAXObjectVectorAttribute::AriaDetails: + // TODO(dmazzoni): implement aria-details. http://crbug.com/644766 + break; case blink::WebAXObjectVectorAttribute::AriaFlowTo: AddIntListAttributeFromWebObjects(ui::AX_ATTR_FLOWTO_IDS, value, dst_); break;
diff --git a/content/renderer/android/app_web_message_port_client.cc b/content/renderer/android/app_web_message_port_client.cc index 4fffaa7..d2b7dd67 100644 --- a/content/renderer/android/app_web_message_port_client.cc +++ b/content/renderer/android/app_web_message_port_client.cc
@@ -12,6 +12,7 @@ #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_view.h" #include "ipc/ipc_message_macros.h" +#include "third_party/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebKit.h" #include "third_party/WebKit/public/web/WebSerializedScriptValue.h" @@ -59,7 +60,8 @@ v8::Local<v8::Context> context = main_frame->mainWorldScriptContext(); v8::Context::Scope context_scope(context); DCHECK(!context.IsEmpty()); - WebSerializedScriptValue v = WebSerializedScriptValue::fromString(message); + WebSerializedScriptValue v = WebSerializedScriptValue::fromString( + blink::WebString::fromUTF16(message)); v8::Local<v8::Value> v8value = v.deserialize(); std::unique_ptr<V8ValueConverter> converter; @@ -99,7 +101,7 @@ converter->ToV8Value(value.get(), context); WebSerializedScriptValue serialized_script_value = WebSerializedScriptValue::serialize(result_value); - base::string16 result = serialized_script_value.toString(); + base::string16 result = serialized_script_value.toString().utf16(); Send(new AppWebMessagePortHostMsg_ConvertedAppToWebMessage( render_frame()->GetRoutingID(), message_port_id, result, sent_message_port_ids));
diff --git a/content/renderer/android/content_detector.cc b/content/renderer/android/content_detector.cc index 22a4fe9..7bf2562 100644 --- a/content/renderer/android/content_detector.cc +++ b/content/renderer/android/content_detector.cc
@@ -36,7 +36,7 @@ if (surrounding_text.isNull()) return false; - base::string16 content = surrounding_text.textContent(); + base::string16 content = surrounding_text.textContent().utf16(); if (content.empty()) return false;
diff --git a/content/renderer/android/renderer_date_time_picker.cc b/content/renderer/android/renderer_date_time_picker.cc index d8c0c65..ad4ed4e5 100644 --- a/content/renderer/android/renderer_date_time_picker.cc +++ b/content/renderer/android/renderer_date_time_picker.cc
@@ -27,8 +27,8 @@ const blink::WebDateTimeSuggestion& suggestion) { DateTimeSuggestion result; result.value = suggestion.value; - result.localized_value = suggestion.localizedValue; - result.label = suggestion.label; + result.localized_value = suggestion.localizedValue.utf16(); + result.label = suggestion.label.utf16(); return result; }
diff --git a/content/renderer/mus/renderer_window_tree_client.cc b/content/renderer/mus/renderer_window_tree_client.cc index 2be8bcf..e9827f8 100644 --- a/content/renderer/mus/renderer_window_tree_client.cc +++ b/content/renderer/mus/renderer_window_tree_client.cc
@@ -27,12 +27,20 @@ // static void RendererWindowTreeClient::Create(int routing_id) { - DCHECK(g_connections.Get().find(routing_id) == g_connections.Get().end()); + DCHECK(g_connections.Get().find(routing_id) == g_connections.Get().end()) + << routing_id; RendererWindowTreeClient* connection = new RendererWindowTreeClient(routing_id); g_connections.Get().insert(std::make_pair(routing_id, connection)); } +// static +void RendererWindowTreeClient::Destroy(int routing_id) { + auto* client = Get(routing_id); + if (client) + client->DestroySelf(); +} + RendererWindowTreeClient::RendererWindowTreeClient(int routing_id) : routing_id_(routing_id), binding_(this) {}
diff --git a/content/renderer/mus/renderer_window_tree_client.h b/content/renderer/mus/renderer_window_tree_client.h index 81662e5..9ab9f3f 100644 --- a/content/renderer/mus/renderer_window_tree_client.h +++ b/content/renderer/mus/renderer_window_tree_client.h
@@ -34,6 +34,9 @@ // connection to mus is lost, or when the window is closed. static void Create(int routing_id); + // Destroys the client instance, if one exists. Otherwise, does nothing. + static void Destroy(int routing_id); + // Returns the RendererWindowTreeClient associated with |routing_id|. Returns // nullptr if none exists. static RendererWindowTreeClient* Get(int routing_id);
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 3457a0f..d28c0f4 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -2864,6 +2864,7 @@ // TODO(avayvod, asvitkine): Query the value directly when it is available // in the renderer process. See https://crbug.com/681160. GetWebkitPreferences().max_keyframe_distance_to_disable_background_video, + GetWebkitPreferences().enable_instant_source_buffer_gc, GetContentClient()->renderer()->AllowMediaSuspend()); bool use_fallback_path = false;
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index 791b768e..45678d1 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc
@@ -706,8 +706,9 @@ AddFilter((new ServiceWorkerContextMessageFilter())->GetFilter()); #if defined(USE_AURA) - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kUseMusInRenderer)) { + if (IsRunningInMash() && + !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kNoUseMusInRenderer)) { CreateRenderWidgetWindowTreeClientFactory(GetServiceManagerConnection()); } #endif @@ -1825,7 +1826,7 @@ "PurgeAndSuspend.Experimental.MemoryGrowth.BlinkGCKB", GET_MEMORY_GROWTH(memory_metrics, purge_and_suspend_memory_metrics_, blink_gc_kb)); - UMA_HISTOGRAM_MEMORY_MB( + UMA_HISTOGRAM_MEMORY_KB( "PurgeAndSuspend.Experimental.MemoryGrowth.MallocKB", GET_MEMORY_GROWTH(memory_metrics, purge_and_suspend_memory_metrics_, malloc_mb) * 1024); @@ -1833,11 +1834,11 @@ "PurgeAndSuspend.Experimental.MemoryGrowth.DiscardableKB", GET_MEMORY_GROWTH(memory_metrics, purge_and_suspend_memory_metrics_, discardable_kb)); - UMA_HISTOGRAM_MEMORY_MB( + UMA_HISTOGRAM_MEMORY_KB( "PurgeAndSuspend.Experimental.MemoryGrowth.V8MainThreadIsolateKB", GET_MEMORY_GROWTH(memory_metrics, purge_and_suspend_memory_metrics_, v8_main_thread_isolate_mb) * 1024); - UMA_HISTOGRAM_MEMORY_MB( + UMA_HISTOGRAM_MEMORY_KB( "PurgeAndSuspend.Experimental.MemoryGrowth.TotalAllocatedKB", GET_MEMORY_GROWTH(memory_metrics, purge_and_suspend_memory_metrics_, total_allocated_mb) * 1024); @@ -1894,7 +1895,8 @@ use_software = true; #if defined(USE_AURA) - if (!use_software && command_line.HasSwitch(switches::kUseMusInRenderer)) { + if (!use_software && IsRunningInMash() && + !command_line.HasSwitch(switches::kNoUseMusInRenderer)) { return RendererWindowTreeClient::Get(routing_id) ->CreateCompositorFrameSink( frame_sink_id,
diff --git a/content/renderer/render_view_browsertest_mac.mm b/content/renderer/render_view_browsertest_mac.mm index 4e1b732..d822280 100644 --- a/content/renderer/render_view_browsertest_mac.mm +++ b/content/renderer/render_view_browsertest_mac.mm
@@ -92,7 +92,7 @@ view->OnUpdateWebPreferences(prefs); const int kMaxOutputCharacters = 1024; - base::string16 output; + std::string output; NSEvent* arrowDownKeyDown = CmdDeadKeyEvent(NSKeyDown, kVK_DownArrow); NSEvent* arrowUpKeyDown = CmdDeadKeyEvent(NSKeyDown, kVK_UpArrow); @@ -109,8 +109,9 @@ ProcessPendingMessages(); ExecuteJavaScriptForTests("scroll.textContent = window.pageYOffset"); output = WebFrameContentDumper::dumpWebViewAsText(view->GetWebView(), - kMaxOutputCharacters); - EXPECT_EQ(kArrowDownScrollDown, base::UTF16ToASCII(output)); + kMaxOutputCharacters) + .ascii(); + EXPECT_EQ(kArrowDownScrollDown, output); const char* kArrowUpScrollUp = "38,false,false,true,false\n0\np1"; view->OnSetEditCommandsForNextKeyEvent( @@ -119,8 +120,9 @@ ProcessPendingMessages(); ExecuteJavaScriptForTests("scroll.textContent = window.pageYOffset"); output = WebFrameContentDumper::dumpWebViewAsText(view->GetWebView(), - kMaxOutputCharacters); - EXPECT_EQ(kArrowUpScrollUp, base::UTF16ToASCII(output)); + kMaxOutputCharacters) + .ascii(); + EXPECT_EQ(kArrowUpScrollUp, output); // Now let javascript eat the key events -- no scrolling should happen. // Set a scroll position slightly down the page to ensure that it does not @@ -134,8 +136,9 @@ ProcessPendingMessages(); ExecuteJavaScriptForTests("scroll.textContent = window.pageYOffset"); output = WebFrameContentDumper::dumpWebViewAsText(view->GetWebView(), - kMaxOutputCharacters); - EXPECT_EQ(kArrowDownNoScroll, base::UTF16ToASCII(output)); + kMaxOutputCharacters) + .ascii(); + EXPECT_EQ(kArrowDownNoScroll, output); const char* kArrowUpNoScroll = "38,false,false,true,false\n100\np1"; view->OnSetEditCommandsForNextKeyEvent( @@ -144,8 +147,9 @@ ProcessPendingMessages(); ExecuteJavaScriptForTests("scroll.textContent = window.pageYOffset"); output = WebFrameContentDumper::dumpWebViewAsText(view->GetWebView(), - kMaxOutputCharacters); - EXPECT_EQ(kArrowUpNoScroll, base::UTF16ToASCII(output)); + kMaxOutputCharacters) + .ascii(); + EXPECT_EQ(kArrowUpNoScroll, output); } } // namespace content
diff --git a/content/renderer/render_view_impl_android.cc b/content/renderer/render_view_impl_android.cc index 7c8d486..e13fea07 100644 --- a/content/renderer/render_view_impl_android.cc +++ b/content/renderer/render_view_impl_android.cc
@@ -76,8 +76,8 @@ blink::WebString clip_html; blink::WebRect clip_rect; webview()->extractSmartClipData(rect, clip_text, clip_html, clip_rect); - Send(new ViewHostMsg_SmartClipDataExtracted( - routing_id_, clip_text, clip_html, clip_rect)); + Send(new ViewHostMsg_SmartClipDataExtracted(routing_id_, clip_text.utf16(), + clip_html.utf16(), clip_rect)); } } // namespace content
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index d9edfea..32caa60d 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc
@@ -389,8 +389,8 @@ render_widget_scheduling_state_->SetHidden(is_hidden_); } #if defined(USE_AURA) - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kUseMusInRenderer)) { + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kNoUseMusInRenderer)) { RendererWindowTreeClient::Create(routing_id_); } #endif @@ -402,6 +402,15 @@ // If we are swapped out, we have released already. if (!is_swapped_out_ && RenderProcess::current()) RenderProcess::current()->ReleaseProcess(); +#if defined(USE_AURA) + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kNoUseMusInRenderer)) { + // It is possible for a RenderWidget to be destroyed before it was embedded + // in a mus window. The RendererWindowTreeClient will leak in such cases. So + // explicitly delete it here. + RendererWindowTreeClient::Destroy(routing_id_); + } +#endif } // static
diff --git a/content/renderer/render_widget_unittest.cc b/content/renderer/render_widget_unittest.cc index 6d227f8bd..2572bc2a 100644 --- a/content/renderer/render_widget_unittest.cc +++ b/content/renderer/render_widget_unittest.cc
@@ -401,7 +401,7 @@ class PopupRenderWidget : public RenderWidget { public: explicit PopupRenderWidget(CompositorDependencies* compositor_deps) - : RenderWidget(1, + : RenderWidget(routing_id_++, compositor_deps, blink::WebPopupTypePage, ScreenInfo(), @@ -432,10 +432,13 @@ private: IPC::TestSink sink_; MockWebWidget mock_webwidget_; + static int routing_id_; DISALLOW_COPY_AND_ASSIGN(PopupRenderWidget); }; +int PopupRenderWidget::routing_id_ = 1; + class RenderWidgetPopupUnittest : public testing::Test { public: RenderWidgetPopupUnittest()
diff --git a/content/shell/browser/shell_url_request_context_getter.cc b/content/shell/browser/shell_url_request_context_getter.cc index ea0c5b8..1943e4fe 100644 --- a/content/shell/browser/shell_url_request_context_getter.cc +++ b/content/shell/browser/shell_url_request_context_getter.cc
@@ -15,7 +15,6 @@ #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/threading/sequenced_worker_pool.h" -#include "base/threading/worker_pool.h" #include "build/build_config.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/cookie_store_factory.h" @@ -129,8 +128,7 @@ new net::URLRequestContextStorage(url_request_context_.get())); storage_->set_cookie_store(CreateCookieStore(CookieStoreConfig())); storage_->set_channel_id_service(base::WrapUnique( - new net::ChannelIDService(new net::DefaultChannelIDStore(NULL), - base::WorkerPool::GetTaskRunner(true)))); + new net::ChannelIDService(new net::DefaultChannelIDStore(NULL)))); url_request_context_->cookie_store()->SetChannelIDServiceID( url_request_context_->channel_id_service()->GetUniqueID()); storage_->set_http_user_agent_settings(
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py index 5790a3bc..34e0634 100644 --- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py +++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -722,6 +722,8 @@ # Linux Intel self.Fail('conformance2/extensions/ext-color-buffer-float.html', ['linux', 'intel'], bug=640389) + self.Fail('WebglExtension_EXT_disjoint_timer_query_webgl2', + ['linux', 'intel'], bug=687210) # See https://bugs.freedesktop.org/show_bug.cgi?id=94477 self.Skip('conformance/glsl/bugs/temp-expressions-should-not-crash.html',
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py index 6beb835..10aa6c7 100644 --- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py +++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -471,6 +471,9 @@ self.Fail('conformance/rendering/clipping-wide-points.html', ['linux', 'intel'], bug=642822) + self.Fail('WebglExtension_EXT_disjoint_timer_query', + ['linux', 'intel'], bug=687210) + #################### # Android failures # ####################
diff --git a/docs/adding_to_third_party.md b/docs/adding_to_third_party.md index 89b9ff19..02bc235 100644 --- a/docs/adding_to_third_party.md +++ b/docs/adding_to_third_party.md
@@ -126,12 +126,14 @@ following sign-offs. Some of these are accessible to Googlers only. Non-Googlers can email one of the people in third_party/OWNERS for help. -* Chrome Eng Review. Googlers should see go/chrome-eng-review +* Chrome Eng Review. Googlers should see go/chrome-eng-review (please include information about the additional checkout size, build times, and binary sizes. Please also make sure that the motivation for your project is clear, e.g., a design doc has been circulated). * open-source-third-party-reviews@google.com (ping the list with relevant details and a link to the CL). * security@chromium.org (ping the list with relevant details and a link to the CL). +Please send separate emails to the three lists. + Third party code is a hot spot for security vulnerabilities. When adding a new package that could potentially carry security risk, make sure to highlight risk to security@chromium.org. You may be asked to add a README.security or, in
diff --git a/extensions/common/features/feature_provider.h b/extensions/common/features/feature_provider.h index 0e629c5..a6944231 100644 --- a/extensions/common/features/feature_provider.h +++ b/extensions/common/features/feature_provider.h
@@ -16,6 +16,8 @@ class Feature; +// Note: Binding code (specifically native_extension_bindings_system.cc) relies +// on this being a sorted map. using FeatureMap = std::map<std::string, std::unique_ptr<Feature>>; // Implemented by classes that can vend features.
diff --git a/extensions/renderer/native_extension_bindings_system.cc b/extensions/renderer/native_extension_bindings_system.cc index 796302dd..e20a54f 100644 --- a/extensions/renderer/native_extension_bindings_system.cc +++ b/extensions/renderer/native_extension_bindings_system.cc
@@ -28,6 +28,34 @@ const char kBindingsSystemPerContextKey[] = "extension_bindings_system"; +// Returns true if the given |api| is a "prefixed" api of the |root_api|; that +// is, if the api begins with the root. +// For example, 'app.runtime' is a prefixed api of 'app'. +// This is designed to be used as a utility when iterating over a sorted map, so +// assumes that |api| is lexicographically greater than |root_api|. +bool IsPrefixedAPI(base::StringPiece api, base::StringPiece root_api) { + DCHECK_NE(api, root_api); + DCHECK_GT(api, root_api); + return base::StartsWith(api, root_api, base::CompareCase::SENSITIVE) && + api[root_api.size()] == '.'; +} + +// Returns the first different level of the api specification between the given +// |api_name| and |reference|. For an api_name of 'app.runtime' and a reference +// of 'app', this returns 'app.runtime'. For an api_name of +// 'cast.streaming.session' and a reference of 'cast', this returns +// 'cast.streaming'. If reference is empty, this simply returns the first layer; +// so given 'app.runtime' and no reference, this returns 'app'. +base::StringPiece GetFirstDifferentAPIName( + base::StringPiece api_name, + base::StringPiece reference) { + base::StringPiece::size_type dot = + api_name.find('.', reference.empty() ? 0 : reference.size() + 1); + if (dot == base::StringPiece::npos) + return api_name; + return api_name.substr(0, dot); +} + struct BindingsSystemPerContextData : public base::SupportsUserData::Data { BindingsSystemPerContextData( base::WeakPtr<NativeExtensionBindingsSystem> bindings_system) @@ -113,7 +141,7 @@ const base::DictionaryValue& GetAPISchema(const std::string& api_name) { const base::DictionaryValue* schema = ExtensionAPI::GetSharedInstance()->GetSchema(api_name); - CHECK(schema); + CHECK(schema) << api_name; return *schema; } @@ -124,6 +152,135 @@ return context->GetAvailability(method_name).is_available(); } +// Creates the binding object for the given |root_name|. This can be +// complicated, since APIs may have prefixed names, like 'app.runtime' or +// 'system.cpu'. This method accepts the first name (i.e., the key that we are +// looking for on the chrome object, such as 'app') and returns the fully +// instantiated binding, including prefixed APIs. That is, given 'app', this +// will instantiate 'app', 'app.runtime', and 'app.window'. +// +// NOTE(devlin): We could do the prefixed apis lazily; however, it's not clear +// how much of a win it would be. It's less overhead here than in the general +// case (instantiating a handful of APIs instead of all of them), and it's more +// likely they will be used (since the extension is already accessing the +// parent). +v8::Local<v8::Object> CreateFullBinding( + v8::Local<v8::Context> context, + ScriptContext* script_context, + APIBindingsSystem* bindings_system, + const FeatureProvider* api_feature_provider, + const std::string& root_name) { + const FeatureMap& features = api_feature_provider->GetAllFeatures(); + auto lower = features.lower_bound(root_name); + DCHECK(lower != features.end()); + + // Some bindings have a prefixed name, like app.runtime, where 'app' and + // 'app.runtime' are, in fact, separate APIs. It's also possible for a + // context to have access to 'app.runtime', but not to 'app'. For this, we + // either instantiate the 'app' binding fully (if the context has access), or + // else use an empty object (so we can still instantiate 'app.runtime'). + v8::Local<v8::Object> root_binding; + if (lower->first == root_name) { + if (script_context->IsAnyFeatureAvailableToContext( + *lower->second, CheckAliasStatus::NOT_ALLOWED)) { + v8::Local<v8::Object> hooks_interface; + v8::Local<v8::Object> binding_object = bindings_system->CreateAPIInstance( + root_name, context, context->GetIsolate(), + base::Bind(&IsAPIMethodAvailable, script_context), &hooks_interface); + + gin::Handle<APIBindingBridge> bridge_handle = gin::CreateHandle( + context->GetIsolate(), + new APIBindingBridge(context, binding_object, hooks_interface, + script_context->GetExtensionID(), + script_context->GetContextTypeDescription(), + base::Bind(&CallJsFunction))); + v8::Local<v8::Value> native_api_bridge = bridge_handle.ToV8(); + script_context->module_system()->OnNativeBindingCreated( + root_name, native_api_bridge); + + root_binding = binding_object; + } + ++lower; + } + + // Look for any bindings that would be on the same object. Any of these would + // start with the same base name (e.g. 'app') + '.' (since '.' is < x for any + // isalpha(x)). + std::string upper = root_name + static_cast<char>('.' + 1); + base::StringPiece last_binding_name; + // The following loop is a little painful because we have crazy binding names + // and syntaxes. The way this works is as follows: + // Look at each feature after the root feature we passed in. If there exists + // a (non-child) feature with a prefixed name, create the full binding for + // the object that the next feature is on. Then, iterate past any features + // already instantiated by that, and continue until there are no more features + // prefixed by the root API. + // As a concrete example, we can look at the cast APIs (cast and + // cast.streaming.*) + // Start with vanilla 'cast', and instantiate that. + // Then iterate over features, and see 'cast.streaming.receiverSession'. + // 'cast.streaming.receiverSession' is a prefixed API of 'cast', but we find + // the first level of difference, which is 'cast.streaming', and instantiate + // that object completely (through recursion). + // The next feature is 'cast.streaming.rtpStream', but this is a prefixed API + // of 'cast.streaming', which we just instantiated completely (including + // 'cast.streaming.rtpStream'), so we continue. + // Iterate until all cast.* features are created. + // TODO(devlin): This is bonkers, but what's the better way? We could extract + // this out to be a more readable Visitor implementation, but is it worth it + // for this one place? Ideally, we'd have a less convoluted feature + // representation (some kind of tree would make this trivial), but for now, we + // have strings. + // On the upside, most APIs are not prefixed at all, and this loop is never + // entered. + for (auto iter = lower; iter != features.end() && iter->first < upper; + ++iter) { + if (iter->second->IsInternal()) + continue; + + if (IsPrefixedAPI(iter->first, last_binding_name)) { + // Instantiating |last_binding_name| must have already instantiated + // iter->first. + continue; + } + + // If this API has a parent feature (and isn't marked 'noparent'), + // then this must be a function or event, so we should not register. + if (api_feature_provider->GetParent(iter->second.get()) != nullptr) + continue; + + base::StringPiece binding_name = + GetFirstDifferentAPIName(iter->first, root_name); + + v8::Local<v8::Object> nested_binding = + CreateFullBinding(context, script_context, bindings_system, + api_feature_provider, binding_name.as_string()); + // It's possible that we don't create a binding if no features or + // prefixed features are available to the context. + if (nested_binding.IsEmpty()) + continue; + + if (root_binding.IsEmpty()) + root_binding = v8::Object::New(context->GetIsolate()); + + // The nested api name contains a '.', e.g. 'app.runtime', but we want to + // expose it on the object simply as 'runtime'. + // Cache the last_binding_name now before mangling it. + last_binding_name = binding_name; + DCHECK_NE(base::StringPiece::npos, binding_name.rfind('.')); + base::StringPiece accessor_name = + binding_name.substr(binding_name.rfind('.') + 1); + v8::Local<v8::String> nested_name = + gin::StringToSymbol(context->GetIsolate(), accessor_name); + v8::Maybe<bool> success = + root_binding->CreateDataProperty(context, nested_name, nested_binding); + if (!success.IsJust() || !success.FromJust()) + return v8::Local<v8::Object>(); + } + + return root_binding; +} + } // namespace NativeExtensionBindingsSystem::NativeExtensionBindingsSystem( @@ -172,7 +329,13 @@ const FeatureProvider* api_feature_provider = FeatureProvider::GetAPIFeatures(); + base::StringPiece last_accessor; for (const auto& map_entry : api_feature_provider->GetAllFeatures()) { + // If we've already set up an accessor for the immediate property of the + // chrome object, we don't need to do more. + if (IsPrefixedAPI(map_entry.first, last_accessor)) + continue; + // Internal APIs are included via require(api_name) from internal code // rather than chrome[api_name]. if (map_entry.second->IsInternal()) @@ -198,8 +361,16 @@ CheckAliasStatus::NOT_ALLOWED)) continue; + // We've found an API that's available to the extension. Normally, we will + // expose this under the name of the feature (e.g., 'tabs'), but in some + // cases, this will be a prefixed API, such as 'app.runtime'. Find what the + // property on the chrome object is named, and use that. So in the case of + // 'app.runtime', we surface a getter for simply 'app'. + base::StringPiece accessor_name = + GetFirstDifferentAPIName(map_entry.first, base::StringPiece()); + last_accessor = accessor_name; v8::Local<v8::String> api_name = - gin::StringToSymbol(v8_context->GetIsolate(), map_entry.first); + gin::StringToSymbol(v8_context->GetIsolate(), accessor_name); v8::Maybe<bool> success = chrome->SetAccessor( v8_context, api_name, &GetAPIHelper, nullptr, api_name); if (!success.IsJust() || !success.FromJust()) { @@ -276,28 +447,22 @@ std::string api_name_string; CHECK(gin::Converter<std::string>::FromV8(isolate, api_name, &api_name_string)); - v8::Local<v8::Object> hooks_interface; - APIBindingsSystem& api_system = data->bindings_system->api_system_; - result = api_system.CreateAPIInstance( - api_name_string, context, isolate, - base::Bind(&IsAPIMethodAvailable, script_context), &hooks_interface); - gin::Handle<APIBindingBridge> bridge_handle = gin::CreateHandle( - isolate, - new APIBindingBridge(context, result, hooks_interface, - script_context->GetExtensionID(), - script_context->GetContextTypeDescription(), - base::Bind(&CallJsFunction))); - v8::Local<v8::Value> native_api_bridge = bridge_handle.ToV8(); - - script_context->module_system()->OnNativeBindingCreated(api_name_string, - native_api_bridge); + v8::Local<v8::Object> root_binding = + CreateFullBinding(context, script_context, + &data->bindings_system->api_system_, + FeatureProvider::GetAPIFeatures(), api_name_string); + if (root_binding.IsEmpty()) + return; v8::Maybe<bool> success = - apis->CreateDataProperty(context, api_name, result); + apis->CreateDataProperty(context, api_name, root_binding); if (!success.IsJust() || !success.FromJust()) return; + + result = root_binding; } + info.GetReturnValue().Set(result); }
diff --git a/extensions/renderer/native_extension_bindings_system_unittest.cc b/extensions/renderer/native_extension_bindings_system_unittest.cc index 2d184a9..78c498c 100644 --- a/extensions/renderer/native_extension_bindings_system_unittest.cc +++ b/extensions/renderer/native_extension_bindings_system_unittest.cc
@@ -28,21 +28,37 @@ namespace { +enum class ItemType { + EXTENSION, + PLATFORM_APP, +}; + // Creates an extension with the given |name| and |permissions|. scoped_refptr<Extension> CreateExtension( const std::string& name, + ItemType type, const std::vector<std::string>& permissions) { DictionaryBuilder manifest; manifest.Set("name", name); manifest.Set("manifest_version", 2); manifest.Set("version", "0.1"); manifest.Set("description", "test extension"); + + if (type == ItemType::PLATFORM_APP) { + DictionaryBuilder background; + background.Set("scripts", ListBuilder().Append("test.js").Build()); + manifest.Set( + "app", + DictionaryBuilder().Set("background", background.Build()).Build()); + } + { ListBuilder permissions_builder; for (const std::string& permission : permissions) permissions_builder.Append(permission); manifest.Set("permissions", permissions_builder.Build()); } + return ExtensionBuilder() .SetManifest(manifest.Build()) .SetLocation(Manifest::INTERNAL) @@ -155,7 +171,7 @@ TEST_F(NativeExtensionBindingsSystemUnittest, Basic) { scoped_refptr<Extension> extension = - CreateExtension("foo", {"idle", "power"}); + CreateExtension("foo", ItemType::EXTENSION, {"idle", "power"}); RegisterExtension(extension->id()); v8::HandleScope handle_scope(isolate()); @@ -248,7 +264,7 @@ TEST_F(NativeExtensionBindingsSystemUnittest, Events) { scoped_refptr<Extension> extension = - CreateExtension("foo", {"idle", "power"}); + CreateExtension("foo", ItemType::EXTENSION, {"idle", "power"}); RegisterExtension(extension->id()); v8::HandleScope handle_scope(isolate()); @@ -289,7 +305,8 @@ // Tests that referencing the same API multiple times returns the same object; // i.e. chrome.foo === chrome.foo. TEST_F(NativeExtensionBindingsSystemUnittest, APIObjectsAreEqual) { - scoped_refptr<Extension> extension = CreateExtension("foo", {"idle"}); + scoped_refptr<Extension> extension = + CreateExtension("foo", ItemType::EXTENSION, {"idle"}); RegisterExtension(extension->id()); v8::HandleScope handle_scope(isolate()); @@ -316,7 +333,7 @@ TEST_F(NativeExtensionBindingsSystemUnittest, ReferencingAPIAfterDisposingContext) { scoped_refptr<Extension> extension = - CreateExtension("foo", {"idle", "power"}); + CreateExtension("foo", ItemType::EXTENSION, {"idle", "power"}); RegisterExtension(extension->id()); @@ -372,7 +389,8 @@ source_map()->RegisterModule("idle", kCustomBinding); - scoped_refptr<Extension> extension = CreateExtension("foo", {"idle"}); + scoped_refptr<Extension> extension = + CreateExtension("foo", ItemType::EXTENSION, {"idle"}); RegisterExtension(extension->id()); v8::HandleScope handle_scope(isolate()); @@ -453,7 +471,7 @@ TEST_F(NativeExtensionBindingsSystemUnittest, TestEventRegistration) { InitEventChangeHandler(); scoped_refptr<Extension> extension = - CreateExtension("foo", {"idle", "power"}); + CreateExtension("foo", ItemType::EXTENSION, {"idle", "power"}); RegisterExtension(extension->id()); @@ -501,4 +519,86 @@ ::testing::Mock::VerifyAndClearExpectations(event_change_handler()); } +TEST_F(NativeExtensionBindingsSystemUnittest, + TestPrefixedApiEventsAndAppBinding) { + InitEventChangeHandler(); + scoped_refptr<Extension> app = CreateExtension("foo", ItemType::PLATFORM_APP, + std::vector<std::string>()); + EXPECT_TRUE(app->is_platform_app()); + RegisterExtension(app->id()); + + v8::HandleScope handle_scope(isolate()); + v8::Local<v8::Context> context = ContextLocal(); + + ScriptContext* script_context = CreateScriptContext( + context, app.get(), Feature::BLESSED_EXTENSION_CONTEXT); + script_context->set_url(app->url()); + + bindings_system()->UpdateBindingsForContext(script_context); + + // The 'chrome.app' object should have 'runtime' and 'window' entries, but + // not the internal 'currentWindowInternal' object. + v8::Local<v8::Value> app_binding_keys = + V8ValueFromScriptSource(context, + "JSON.stringify(Object.keys(chrome.app));"); + ASSERT_FALSE(app_binding_keys.IsEmpty()); + ASSERT_TRUE(app_binding_keys->IsString()); + EXPECT_EQ("[\"runtime\",\"window\"]", + gin::V8ToString(app_binding_keys)); + + const char kUseAppRuntime[] = + "(function() {\n" + " chrome.app.runtime.onLaunched.addListener(function() {});\n" + "});"; + v8::Local<v8::Function> use_app_runtime = + FunctionFromString(context, kUseAppRuntime); + EXPECT_CALL(*event_change_handler(), + OnChange(binding::EventListenersChanged::HAS_LISTENERS, + script_context, "app.runtime.onLaunched")) + .Times(1); + RunFunctionOnGlobal(use_app_runtime, context, 0, nullptr); + ::testing::Mock::VerifyAndClearExpectations(event_change_handler()); +} + +TEST_F(NativeExtensionBindingsSystemUnittest, + TestPrefixedApiMethodsAndSystemBinding) { + scoped_refptr<Extension> extension = + CreateExtension("foo", ItemType::EXTENSION, {"system.cpu"}); + RegisterExtension(extension->id()); + + v8::HandleScope handle_scope(isolate()); + v8::Local<v8::Context> context = ContextLocal(); + + ScriptContext* script_context = CreateScriptContext( + context, extension.get(), Feature::BLESSED_EXTENSION_CONTEXT); + script_context->set_url(extension->url()); + + bindings_system()->UpdateBindingsForContext(script_context); + + // The system.cpu object should exist, but system.network should not (as the + // extension didn't request permission to it). + v8::Local<v8::Value> system_cpu = + V8ValueFromScriptSource(context, "chrome.system.cpu"); + ASSERT_FALSE(system_cpu.IsEmpty()); + EXPECT_TRUE(system_cpu->IsObject()); + EXPECT_FALSE(system_cpu->IsUndefined()); + + v8::Local<v8::Value> system_network = + V8ValueFromScriptSource(context, "chrome.system.network"); + ASSERT_FALSE(system_network.IsEmpty()); + EXPECT_TRUE(system_network->IsUndefined()); + + const char kUseSystemCpu[] = + "(function() {\n" + " chrome.system.cpu.getInfo(function() {})\n" + "});"; + v8::Local<v8::Function> use_system_cpu = + FunctionFromString(context, kUseSystemCpu); + RunFunctionOnGlobal(use_system_cpu, context, 0, nullptr); + + EXPECT_EQ(extension->id(), last_params().extension_id); + EXPECT_EQ("system.cpu.getInfo", last_params().name); + EXPECT_TRUE(last_params().has_callback); +} + } // namespace extensions
diff --git a/google_apis/gcm/tools/mcs_probe.cc b/google_apis/gcm/tools/mcs_probe.cc index 4dbcf31..2777b46b 100644 --- a/google_apis/gcm/tools/mcs_probe.cc +++ b/google_apis/gcm/tools/mcs_probe.cc
@@ -26,7 +26,6 @@ #include "base/strings/string_number_conversions.h" #include "base/threading/thread.h" #include "base/threading/thread_task_runner_handle.h" -#include "base/threading/worker_pool.h" #include "base/time/default_clock.h" #include "base/values.h" #include "build/build_config.h" @@ -399,9 +398,7 @@ cert_verifier_ = net::CertVerifier::CreateDefault(); } system_channel_id_service_.reset( - new net::ChannelIDService( - new net::DefaultChannelIDStore(NULL), - base::WorkerPool::GetTaskRunner(true))); + new net::ChannelIDService(new net::DefaultChannelIDStore(NULL))); transport_security_state_.reset(new net::TransportSecurityState()); cert_transparency_verifier_.reset(new net::MultiLogCTVerifier());
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index 8a03511..11fa18c 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -2779,10 +2779,13 @@ bool BackTexture::AllocateNativeGpuMemoryBuffer(const gfx::Size& size, GLenum format, bool zero) { - gfx::BufferFormat buffer_format = gfx::BufferFormat::RGBA_8888; + DCHECK(format == GL_RGB || format == GL_RGBA); scoped_refptr<gl::GLImage> image = decoder_->GetContextGroup()->image_factory()->CreateAnonymousImage( - size, buffer_format, format); + size, + format == GL_RGB ? gfx::BufferFormat::RGBX_8888 + : gfx::BufferFormat::RGBA_8888, + format); if (!image || !image->BindTexImage(Target())) return false;
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.mm b/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.mm index d2b2678..6bc5e92 100644 --- a/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.mm +++ b/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.mm
@@ -14,7 +14,6 @@ #include "base/memory/ptr_util.h" #include "base/sequenced_task_runner.h" #include "base/threading/sequenced_worker_pool.h" -#include "base/threading/worker_pool.h" #include "components/cookie_config/cookie_store_util.h" #include "components/net_log/chrome_net_log.h" #include "components/prefs/json_pref_store.h" @@ -369,8 +368,7 @@ web::WebThread::GetBlockingPool()->GetSequencedTaskRunner( web::WebThread::GetBlockingPool()->GetSequenceToken())); channel_id_service = new net::ChannelIDService( - new net::DefaultChannelIDStore(channel_id_db.get()), - base::WorkerPool::GetTaskRunner(true)); + new net::DefaultChannelIDStore(channel_id_db.get())); } set_channel_id_service(channel_id_service); @@ -420,8 +418,7 @@ // Use a separate ChannelIDService. std::unique_ptr<net::ChannelIDService> channel_id_service( - new net::ChannelIDService(new net::DefaultChannelIDStore(nullptr), - base::WorkerPool::GetTaskRunner(true))); + new net::ChannelIDService(new net::DefaultChannelIDStore(nullptr))); // Build a new HttpNetworkSession that uses the new ChannelIDService. net::HttpNetworkSession::Params network_params =
diff --git a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.mm b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.mm index 781e2dc..ffb1566 100644 --- a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.mm +++ b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.mm
@@ -13,7 +13,6 @@ #include "base/logging.h" #include "base/mac/bind_objc_block.h" #include "base/stl_util.h" -#include "base/threading/worker_pool.h" #include "components/net_log/chrome_net_log.h" #include "components/prefs/pref_service.h" #include "ios/chrome/browser/browser_state/chrome_browser_state.h" @@ -191,8 +190,7 @@ web::WebThread::GetTaskRunnerForThread(web::WebThread::DB)); net::ChannelIDService* channel_id_service = new net::ChannelIDService( - new net::DefaultChannelIDStore(channel_id_store.get()), - base::WorkerPool::GetTaskRunner(true)); + new net::DefaultChannelIDStore(channel_id_store.get())); set_channel_id_service(channel_id_service); main_context->set_channel_id_service(channel_id_service);
diff --git a/ios/chrome/browser/ios_chrome_io_thread.mm b/ios/chrome/browser/ios_chrome_io_thread.mm index b16594a60..4a768dc 100644 --- a/ios/chrome/browser/ios_chrome_io_thread.mm +++ b/ios/chrome/browser/ios_chrome_io_thread.mm
@@ -25,7 +25,6 @@ #include "base/strings/string_util.h" #include "base/threading/sequenced_worker_pool.h" #include "base/threading/thread.h" -#include "base/threading/worker_pool.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "components/net_log/chrome_net_log.h" @@ -385,8 +384,7 @@ globals_->system_cookie_store.reset(new net::CookieMonster(nullptr, nullptr)); // In-memory channel ID store. globals_->system_channel_id_service.reset( - new net::ChannelIDService(new net::DefaultChannelIDStore(nullptr), - base::WorkerPool::GetTaskRunner(true))); + new net::ChannelIDService(new net::DefaultChannelIDStore(nullptr))); globals_->system_cookie_store->SetChannelIDServiceID( globals_->system_channel_id_service->GetUniqueID()); globals_->http_user_agent_settings.reset(new net::StaticHttpUserAgentSettings(
diff --git a/ios/crnet/crnet_environment.mm b/ios/crnet/crnet_environment.mm index 94572a2..ef674a1d 100644 --- a/ios/crnet/crnet_environment.mm +++ b/ios/crnet/crnet_environment.mm
@@ -25,7 +25,6 @@ #include "base/metrics/statistics_recorder.h" #include "base/path_service.h" #include "base/single_thread_task_runner.h" -#include "base/threading/worker_pool.h" #include "components/prefs/json_pref_store.h" #include "components/prefs/pref_filter.h" #include "ios/crnet/sdch_owner_pref_storage.h" @@ -462,9 +461,8 @@ // constructed. If not, build an ephemeral ChannelIDService with no backing // disk store. // TODO(ellyjones): support persisting ChannelID. - params.channel_id_service = new net::ChannelIDService( - new net::DefaultChannelIDStore(NULL), - base::WorkerPool::GetTaskRunner(true)); + params.channel_id_service = + new net::ChannelIDService(new net::DefaultChannelIDStore(NULL)); } // TODO(mmenke): These really shouldn't be leaked.
diff --git a/ios/web/shell/shell_url_request_context_getter.mm b/ios/web/shell/shell_url_request_context_getter.mm index d28a0b4..41c093a1 100644 --- a/ios/web/shell/shell_url_request_context_getter.mm +++ b/ios/web/shell/shell_url_request_context_getter.mm
@@ -12,7 +12,6 @@ #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" #include "base/path_service.h" -#include "base/threading/worker_pool.h" #import "ios/net/cookies/cookie_store_ios_persistent.h" #import "ios/web/public/web_client.h" #include "ios/web/public/web_thread.h" @@ -109,8 +108,7 @@ url_request_context_->transport_security_state(), base_path_, file_task_runner_, false)); storage_->set_channel_id_service(base::MakeUnique<net::ChannelIDService>( - new net::DefaultChannelIDStore(nullptr), - base::WorkerPool::GetTaskRunner(true))); + new net::DefaultChannelIDStore(nullptr))); storage_->set_http_server_properties( std::unique_ptr<net::HttpServerProperties>( new net::HttpServerPropertiesImpl()));
diff --git a/ios/web_view/internal/criwv_url_request_context_getter.mm b/ios/web_view/internal/criwv_url_request_context_getter.mm index f34be3d..9788424 100644 --- a/ios/web_view/internal/criwv_url_request_context_getter.mm +++ b/ios/web_view/internal/criwv_url_request_context_getter.mm
@@ -11,7 +11,6 @@ #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" #include "base/path_service.h" -#include "base/threading/worker_pool.h" #import "ios/net/cookies/cookie_store_ios_persistent.h" #import "ios/web/public/web_client.h" #include "ios/web/public/web_thread.h" @@ -99,8 +98,7 @@ transport_security_persister_.reset(new net::TransportSecurityPersister( transport_security_state.get(), base_path_, file_task_runner_, false)); storage_->set_channel_id_service(base::MakeUnique<net::ChannelIDService>( - new net::DefaultChannelIDStore(nullptr), - base::WorkerPool::GetTaskRunner(true))); + new net::DefaultChannelIDStore(nullptr))); storage_->set_http_server_properties( std::unique_ptr<net::HttpServerProperties>( new net::HttpServerPropertiesImpl()));
diff --git a/ipc/ipc_mojo_bootstrap.cc b/ipc/ipc_mojo_bootstrap.cc index 5c0b1338..5fa88ea 100644 --- a/ipc/ipc_mojo_bootstrap.cc +++ b/ipc/ipc_mojo_bootstrap.cc
@@ -156,9 +156,16 @@ base::AutoLock locker(lock_); bool inserted = false; Endpoint* endpoint = FindOrInsertEndpoint(id, &inserted); - if (inserted && encountered_error_) - endpoint->set_peer_closed(); + if (inserted) { + DCHECK(!endpoint->handle_created()); + if (encountered_error_) + endpoint->set_peer_closed(); + } else { + if (endpoint->handle_created()) + return mojo::ScopedInterfaceEndpointHandle(); + } + endpoint->set_handle_created(); return CreateScopedInterfaceEndpointHandle(id, true); } @@ -240,6 +247,65 @@ friend class Endpoint; friend class ControlMessageProxyThunk; + // Message objects cannot be destroyed under the controller's lock, if they + // contain ScopedInterfaceEndpointHandle objects. + // IncomingMessageWrapper is used to wrap messages which haven't got the + // payload interface IDs deserialized into ScopedInterfaceEndpointHandles. + // Wrapper objects are always destroyed under the controller's lock. When a + // wrapper is destroyed and the message hasn't been consumed, the wrapper is + // responsible to send endpoint closed notifications. + class IncomingMessageWrapper { + public: + IncomingMessageWrapper() = default; + + IncomingMessageWrapper(ChannelAssociatedGroupController* controller, + mojo::Message* message) + : controller_(controller), value_(std::move(*message)) { + DCHECK(value_.associated_endpoint_handles()->empty()); + } + + IncomingMessageWrapper(IncomingMessageWrapper&& other) + : controller_(other.controller_), value_(std::move(other.value_)) {} + + ~IncomingMessageWrapper() { + if (value_.IsNull()) + return; + + controller_->lock_.AssertAcquired(); + + uint32_t num_ids = value_.payload_num_interface_ids(); + const uint32_t* ids = value_.payload_interface_ids(); + for (uint32_t i = 0; i < num_ids; ++i) { + base::AutoUnlock unlocker(controller_->lock_); + controller_->control_message_proxy_.NotifyPeerEndpointClosed( + ids[i], base::nullopt); + } + } + + IncomingMessageWrapper& operator=(IncomingMessageWrapper&& other) { + controller_ = other.controller_; + value_ = std::move(other.value_); + return *this; + } + + // Must be called outside of the controller's lock. + bool TakeMessage(mojo::Message* output) { + DCHECK(!value_.IsNull()); + + *output = std::move(value_); + return output->DeserializeAssociatedEndpointHandles(controller_); + } + + const mojo::Message& value() const { return value_; } + + private: + ChannelAssociatedGroupController* controller_ = nullptr; + // It must not hold any ScopedInterfaceEndpointHandle objects. + mojo::Message value_; + + DISALLOW_COPY_AND_ASSIGN(IncomingMessageWrapper); + }; + class Endpoint : public base::RefCountedThreadSafe<Endpoint>, public mojo::InterfaceEndpointController { public: @@ -268,6 +334,16 @@ peer_closed_ = true; } + bool handle_created() const { + controller_->lock_.AssertAcquired(); + return handle_created_; + } + + void set_handle_created() { + controller_->lock_.AssertAcquired(); + handle_created_ = true; + } + const base::Optional<mojo::DisconnectReason>& disconnect_reason() const { return disconnect_reason_; } @@ -308,7 +384,7 @@ sync_watcher_.reset(); } - uint32_t EnqueueSyncMessage(mojo::Message message) { + uint32_t EnqueueSyncMessage(IncomingMessageWrapper message) { controller_->lock_.AssertAcquired(); uint32_t id = GenerateSyncMessageId(); sync_messages_.emplace(id, std::move(message)); @@ -322,11 +398,11 @@ sync_message_event_->Signal(); } - mojo::Message PopSyncMessage(uint32_t id) { + IncomingMessageWrapper PopSyncMessage(uint32_t id) { controller_->lock_.AssertAcquired(); if (sync_messages_.empty() || sync_messages_.front().first != id) - return mojo::Message(); - mojo::Message message = std::move(sync_messages_.front().second); + return IncomingMessageWrapper(); + IncomingMessageWrapper message = std::move(sync_messages_.front().second); sync_messages_.pop(); return message; } @@ -335,6 +411,7 @@ bool SendMessage(mojo::Message* message) override { DCHECK(task_runner_->BelongsToCurrentThread()); message->set_interface_id(id_); + message->SerializeAssociatedEndpointHandles(controller_); return controller_->SendMessage(message); } @@ -380,14 +457,17 @@ base::AutoLock locker(controller_->lock_); bool more_to_process = false; if (!sync_messages_.empty()) { - mojo::Message message = std::move(sync_messages_.front().second); + IncomingMessageWrapper message_wrapper = + std::move(sync_messages_.front().second); sync_messages_.pop(); bool dispatch_succeeded; mojo::InterfaceEndpointClient* client = client_; { base::AutoUnlock unlocker(controller_->lock_); - dispatch_succeeded = client->HandleIncomingMessage(&message); + mojo::Message message; + dispatch_succeeded = message_wrapper.TakeMessage(&message) && + client->HandleIncomingMessage(&message); } if (!sync_messages_.empty()) @@ -449,12 +529,13 @@ bool closed_ = false; bool peer_closed_ = false; + bool handle_created_ = false; base::Optional<mojo::DisconnectReason> disconnect_reason_; mojo::InterfaceEndpointClient* client_ = nullptr; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; std::unique_ptr<mojo::SyncHandleWatcher> sync_watcher_; std::unique_ptr<MojoEvent> sync_message_event_; - std::queue<std::pair<uint32_t, mojo::Message>> sync_messages_; + std::queue<std::pair<uint32_t, IncomingMessageWrapper>> sync_messages_; uint32_t next_sync_message_id_ = 0; DISALLOW_COPY_AND_ASSIGN(Endpoint); @@ -469,6 +550,7 @@ private: // MessageReceiver: bool Accept(mojo::Message* message) override { + message->SerializeAssociatedEndpointHandles(controller_); return controller_->SendMessage(message); } @@ -622,8 +704,10 @@ bool Accept(mojo::Message* message) override { DCHECK(thread_checker_.CalledOnValidThread()); - if (mojo::PipeControlMessageHandler::IsPipeControlMessage(message)) - return control_message_handler_.Accept(message); + if (mojo::PipeControlMessageHandler::IsPipeControlMessage(message)) { + return message->DeserializeAssociatedEndpointHandles(this) && + control_message_handler_.Accept(message); + } mojo::InterfaceId id = message->interface_id(); DCHECK(mojo::IsValidInterfaceId(id)); @@ -642,12 +726,14 @@ DCHECK(proxy_task_runner_); if (message->has_flag(mojo::Message::kFlagIsSync)) { + IncomingMessageWrapper message_wrapper(this, message); // Sync messages may need to be handled by the endpoint if it's blocking // on a sync reply. We pass ownership of the message to the endpoint's // sync message queue. If the endpoint was blocking, it will dequeue the // message and dispatch it. Otherwise the posted |AcceptSyncMessage()| // call will dequeue the message and dispatch it. - uint32_t message_id = endpoint->EnqueueSyncMessage(std::move(*message)); + uint32_t message_id = + endpoint->EnqueueSyncMessage(std::move(message_wrapper)); proxy_task_runner_->PostTask( FROM_HERE, base::Bind(&ChannelAssociatedGroupController::AcceptSyncMessage, @@ -668,7 +754,8 @@ !message->has_flag(mojo::Message::kFlagIsResponse)); base::AutoUnlock unlocker(lock_); - return client->HandleIncomingMessage(message); + return message->DeserializeAssociatedEndpointHandles(this) && + client->HandleIncomingMessage(message); } void AcceptOnProxyThread(mojo::Message message) { @@ -678,6 +765,8 @@ DCHECK(mojo::IsValidInterfaceId(id) && !mojo::IsMasterInterfaceId(id)); base::AutoLock locker(lock_); + IncomingMessageWrapper message_wrapper(this, &message); + Endpoint* endpoint = GetEndpointForDispatch(id, false /* create */); if (!endpoint) return; @@ -689,12 +778,14 @@ DCHECK(endpoint->task_runner()->BelongsToCurrentThread()); // Sync messages should never make their way to this method. - DCHECK(!message.has_flag(mojo::Message::kFlagIsSync)); + DCHECK(!message_wrapper.value().has_flag(mojo::Message::kFlagIsSync)); bool result = false; { base::AutoUnlock unlocker(lock_); - result = client->HandleIncomingMessage(&message); + mojo::Message message; + result = message_wrapper.TakeMessage(&message) && + client->HandleIncomingMessage(&message); } if (!result) @@ -711,11 +802,12 @@ return; DCHECK(endpoint->task_runner()->BelongsToCurrentThread()); - mojo::Message message = endpoint->PopSyncMessage(message_id); + IncomingMessageWrapper message_wrapper = + endpoint->PopSyncMessage(message_id); // The message must have already been dequeued by the endpoint waking up // from a sync wait. Nothing to do. - if (message.IsNull()) + if (message_wrapper.value().IsNull()) return; mojo::InterfaceEndpointClient* client = endpoint->client(); @@ -725,7 +817,9 @@ bool result = false; { base::AutoUnlock unlocker(lock_); - result = client->HandleIncomingMessage(&message); + mojo::Message message; + result = message_wrapper.TakeMessage(&message) && + client->HandleIncomingMessage(&message); } if (!result)
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc index 30431d7..936d5c0 100644 --- a/media/base/media_switches.cc +++ b/media/base/media_switches.cc
@@ -169,6 +169,12 @@ const base::Feature kBackgroundVideoTrackOptimization{ "BackgroundVideoTrackOptimization", base::FEATURE_DISABLED_BY_DEFAULT}; +// Make MSE garbage collection algorithm more aggressive when we are under +// moderate or critical memory pressure. This will relieve memory pressure by +// releasing stale data from MSE buffers. +const base::Feature kMemoryPressureBasedSourceBufferGC{ + "MemoryPressureBasedSourceBufferGC", base::FEATURE_DISABLED_BY_DEFAULT}; + // Use shared block-based buffering for media. const base::Feature kUseNewMediaCache{"use-new-media-cache", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/media/base/media_switches.h b/media/base/media_switches.h index 7964461..3cf70a5 100644 --- a/media/base/media_switches.h +++ b/media/base/media_switches.h
@@ -93,6 +93,7 @@ MEDIA_EXPORT extern const base::Feature kVideoBlitColorAccuracy; MEDIA_EXPORT extern const base::Feature kExternalClearKeyForTesting; MEDIA_EXPORT extern const base::Feature kBackgroundVideoTrackOptimization; +MEDIA_EXPORT extern const base::Feature kMemoryPressureBasedSourceBufferGC; #if defined(OS_ANDROID) MEDIA_EXPORT extern const base::Feature kAndroidMediaPlayerRenderer;
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc index 28758625..9ecacb59 100644 --- a/media/blink/webmediaplayer_impl.cc +++ b/media/blink/webmediaplayer_impl.cc
@@ -18,6 +18,7 @@ #include "base/debug/alias.h" #include "base/debug/crash_logging.h" #include "base/location.h" +#include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" @@ -237,7 +238,9 @@ preroll_attempt_pending_(false), observer_(params.media_observer()), max_keyframe_distance_to_disable_background_video_( - params.max_keyframe_distance_to_disable_background_video()) { + params.max_keyframe_distance_to_disable_background_video()), + enable_instant_source_buffer_gc_( + params.enable_instant_source_buffer_gc()) { DCHECK(!adjust_allocated_memory_cb_.is_null()); DCHECK(renderer_factory_); DCHECK(client_); @@ -1160,6 +1163,36 @@ new WebMediaSourceImpl(chunk_demuxer_, media_log_)); } +void WebMediaPlayerImpl::OnMemoryPressure( + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { + DVLOG(2) << __func__ << " memory_pressure_level=" << memory_pressure_level; + DCHECK(main_task_runner_->BelongsToCurrentThread()); + DCHECK(base::FeatureList::IsEnabled(kMemoryPressureBasedSourceBufferGC)); + DCHECK(chunk_demuxer_); + + // The new value of |memory_pressure_level| will take effect on the next + // garbage collection. Typically this means the next SourceBuffer append() + // operation, since per MSE spec, the garbage collection must only occur + // during SourceBuffer append(). But if memory pressure is critical it might + // be better to perform GC immediately rather than wait for the next append + // and potentially get killed due to out-of-memory. + // So if this experiment is enabled and pressure level is critical, we'll pass + // down force_instant_gc==true, which will force immediate GC on + // SourceBufferStreams. + bool force_instant_gc = + (enable_instant_source_buffer_gc_ && + memory_pressure_level == + base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); + + // base::Unretained is safe, since chunk_demuxer_ is actually owned by + // |this| via this->demuxer_. + media_task_runner_->PostTask( + FROM_HERE, base::Bind(&ChunkDemuxer::OnMemoryPressure, + base::Unretained(chunk_demuxer_), + base::TimeDelta::FromSecondsD(currentTime()), + memory_pressure_level, force_instant_gc)); +} + void WebMediaPlayerImpl::OnError(PipelineStatus status) { DVLOG(1) << __func__; DCHECK(main_task_runner_->BelongsToCurrentThread()); @@ -1714,6 +1747,13 @@ base::Bind(&WebMediaPlayerImpl::OnDemuxerOpened, AsWeakPtr())), encrypted_media_init_data_cb, media_log_); demuxer_.reset(chunk_demuxer_); + + if (base::FeatureList::IsEnabled(kMemoryPressureBasedSourceBufferGC)) { + // base::Unretained is safe because |this| owns memory_pressure_listener_. + memory_pressure_listener_ = + base::MakeUnique<base::MemoryPressureListener>(base::Bind( + &WebMediaPlayerImpl::OnMemoryPressure, base::Unretained(this))); + } } // TODO(sandersd): FileSystem objects may also be non-static, but due to our
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h index a20739f..1b184a8 100644 --- a/media/blink/webmediaplayer_impl.h +++ b/media/blink/webmediaplayer_impl.h
@@ -15,6 +15,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/linked_ptr.h" +#include "base/memory/memory_pressure_listener.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" @@ -352,6 +353,9 @@ void ReportMemoryUsage(); void FinishMemoryUsageReport(int64_t demuxer_memory_usage); + void OnMemoryPressure( + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level); + // Called during OnHidden() when we want a suspended player to enter the // paused state after some idle timeout. void ScheduleIdlePauseTimer(); @@ -568,6 +572,8 @@ std::unique_ptr<Demuxer> demuxer_; ChunkDemuxer* chunk_demuxer_; + std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_; + BufferedDataSourceHostImpl buffered_data_source_host_; linked_ptr<UrlIndex> url_index_; @@ -671,6 +677,12 @@ // playback optimizations. base::TimeDelta max_keyframe_distance_to_disable_background_video_; + // When MSE memory pressure based garbage collection is enabled, the + // |enable_instant_source_buffer_gc| controls whether the GC is done + // immediately on memory pressure notification or during the next SourceBuffer + // append (slower, but MSE spec compliant). + bool enable_instant_source_buffer_gc_ = false; + // Whether disabled the video track as an optimization. bool video_track_disabled_ = false;
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc index 4f97436..ffca873 100644 --- a/media/blink/webmediaplayer_impl_unittest.cc +++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -209,7 +209,7 @@ media_thread_.task_runner(), message_loop_.task_runner(), message_loop_.task_runner(), WebMediaPlayerParams::Context3DCB(), base::Bind(&OnAdjustAllocatedMemory), nullptr, nullptr, nullptr, - base::TimeDelta::FromSeconds(10), allow_suspend)); + base::TimeDelta::FromSeconds(10), false, allow_suspend)); } ~WebMediaPlayerImplTest() override {
diff --git a/media/blink/webmediaplayer_params.cc b/media/blink/webmediaplayer_params.cc index ac83ceb1..ebace61 100644 --- a/media/blink/webmediaplayer_params.cc +++ b/media/blink/webmediaplayer_params.cc
@@ -24,6 +24,7 @@ SurfaceManager* surface_manager, base::WeakPtr<MediaObserver> media_observer, base::TimeDelta max_keyframe_distance_to_disable_background_video, + bool enable_instant_source_buffer_gc, bool allow_suspend) : defer_load_cb_(defer_load_cb), audio_renderer_sink_(audio_renderer_sink), @@ -38,6 +39,7 @@ media_observer_(media_observer), max_keyframe_distance_to_disable_background_video_( max_keyframe_distance_to_disable_background_video), + enable_instant_source_buffer_gc_(enable_instant_source_buffer_gc), allow_suspend_(allow_suspend) {} WebMediaPlayerParams::~WebMediaPlayerParams() {}
diff --git a/media/blink/webmediaplayer_params.h b/media/blink/webmediaplayer_params.h index a39b6a6..00aa774 100644 --- a/media/blink/webmediaplayer_params.h +++ b/media/blink/webmediaplayer_params.h
@@ -60,6 +60,7 @@ SurfaceManager* surface_manager, base::WeakPtr<MediaObserver> media_observer, base::TimeDelta max_keyframe_distance_to_disable_background_video, + bool enable_instant_source_buffer_gc, bool allow_suspend); ~WebMediaPlayerParams(); @@ -108,6 +109,10 @@ return max_keyframe_distance_to_disable_background_video_; } + bool enable_instant_source_buffer_gc() const { + return enable_instant_source_buffer_gc_; + } + bool allow_suspend() const { return allow_suspend_; } private: @@ -124,6 +129,7 @@ SurfaceManager* surface_manager_; base::WeakPtr<MediaObserver> media_observer_; base::TimeDelta max_keyframe_distance_to_disable_background_video_; + bool enable_instant_source_buffer_gc_; const bool allow_suspend_; DISALLOW_IMPLICIT_CONSTRUCTORS(WebMediaPlayerParams);
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc index 1edff0641..1302aa5 100644 --- a/media/filters/chunk_demuxer.cc +++ b/media/filters/chunk_demuxer.cc
@@ -125,6 +125,15 @@ return stream_->GarbageCollectIfNeeded(media_time, newDataSize); } +void ChunkDemuxerStream::OnMemoryPressure( + DecodeTimestamp media_time, + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level, + bool force_instant_gc) { + base::AutoLock auto_lock(lock_); + return stream_->OnMemoryPressure(media_time, memory_pressure_level, + force_instant_gc); +} + void ChunkDemuxerStream::OnSetDuration(TimeDelta duration) { base::AutoLock auto_lock(lock_); stream_->OnSetDuration(duration); @@ -736,6 +745,19 @@ } } +void ChunkDemuxer::OnMemoryPressure( + base::TimeDelta currentMediaTime, + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level, + bool force_instant_gc) { + DecodeTimestamp media_time_dts = + DecodeTimestamp::FromPresentationTime(currentMediaTime); + base::AutoLock auto_lock(lock_); + for (const auto& itr : source_state_map_) { + itr.second->OnMemoryPressure(media_time_dts, memory_pressure_level, + force_instant_gc); + } +} + bool ChunkDemuxer::EvictCodedFrames(const std::string& id, base::TimeDelta currentMediaTime, size_t newDataSize) {
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h index 020aa14..32eaded 100644 --- a/media/filters/chunk_demuxer.h +++ b/media/filters/chunk_demuxer.h
@@ -16,6 +16,7 @@ #include <vector> #include "base/macros.h" +#include "base/memory/memory_pressure_listener.h" #include "base/synchronization/lock.h" #include "media/base/byte_queue.h" #include "media/base/demuxer.h" @@ -66,6 +67,11 @@ // https://w3c.github.io/media-source/#sourcebuffer-coded-frame-eviction bool EvictCodedFrames(DecodeTimestamp media_time, size_t newDataSize); + void OnMemoryPressure( + DecodeTimestamp media_time, + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level, + bool force_instant_gc); + // Signal to the stream that duration has changed to |duration|. void OnSetDuration(base::TimeDelta duration); @@ -270,6 +276,11 @@ base::TimeDelta currentMediaTime, size_t newDataSize); + void OnMemoryPressure( + base::TimeDelta currentMediaTime, + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level, + bool force_instant_gc); + // Returns the current presentation duration. double GetDuration(); double GetDuration_Locked();
diff --git a/media/filters/source_buffer_state.cc b/media/filters/source_buffer_state.cc index 5d692c4..868abd7 100644 --- a/media/filters/source_buffer_state.cc +++ b/media/filters/source_buffer_state.cc
@@ -306,6 +306,26 @@ return success; } +void SourceBufferState::OnMemoryPressure( + DecodeTimestamp media_time, + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level, + bool force_instant_gc) { + // Notify video streams about memory pressure first, since video typically + // takes up the most memory and that's where we can expect most savings. + for (const auto& it : video_streams_) { + it.second->OnMemoryPressure(media_time, memory_pressure_level, + force_instant_gc); + } + for (const auto& it : audio_streams_) { + it.second->OnMemoryPressure(media_time, memory_pressure_level, + force_instant_gc); + } + for (const auto& it : text_streams_) { + it.second->OnMemoryPressure(media_time, memory_pressure_level, + force_instant_gc); + } +} + Ranges<TimeDelta> SourceBufferState::GetBufferedRanges(TimeDelta duration, bool ended) const { RangesList ranges_list;
diff --git a/media/filters/source_buffer_state.h b/media/filters/source_buffer_state.h index 269cf326..81d50596 100644 --- a/media/filters/source_buffer_state.h +++ b/media/filters/source_buffer_state.h
@@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/macros.h" +#include "base/memory/memory_pressure_listener.h" #include "media/base/audio_codecs.h" #include "media/base/demuxer.h" #include "media/base/demuxer_stream.h" @@ -75,6 +76,17 @@ // https://w3c.github.io/media-source/#sourcebuffer-coded-frame-eviction bool EvictCodedFrames(DecodeTimestamp media_time, size_t newDataSize); + // Gets invoked when the system is experiencing memory pressure, i.e. there's + // not enough free memory. The |media_time| is the media playback position at + // the time of memory pressure notification (needed for accurate GC). The + // |memory_pressure_listener| indicates memory pressure severity. The + // |force_instant_gc| is used to force the MSE garbage collection algorithm to + // be run right away, without waiting for the next append. + void OnMemoryPressure( + DecodeTimestamp media_time, + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level, + bool force_instant_gc); + // Returns true if currently parsing a media segment, or false otherwise. bool parsing_media_segment() const { return parsing_media_segment_; }
diff --git a/media/filters/source_buffer_stream.cc b/media/filters/source_buffer_stream.cc index fe3dbfe..471ae384 100644 --- a/media/filters/source_buffer_stream.cc +++ b/media/filters/source_buffer_stream.cc
@@ -12,6 +12,7 @@ #include "base/bind.h" #include "base/logging.h" #include "base/trace_event/trace_event.h" +#include "media/base/media_switches.h" #include "media/base/timestamp_constants.h" #include "media/filters/source_buffer_platform.h" #include "media/filters/source_buffer_range.h" @@ -692,12 +693,26 @@ } } +void SourceBufferStream::OnMemoryPressure( + DecodeTimestamp media_time, + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level, + bool force_instant_gc) { + DVLOG(4) << __func__ << " level=" << memory_pressure_level; + memory_pressure_level_ = memory_pressure_level; + + if (force_instant_gc) + GarbageCollectIfNeeded(media_time, 0); +} + bool SourceBufferStream::GarbageCollectIfNeeded(DecodeTimestamp media_time, size_t newDataSize) { DCHECK(media_time != kNoDecodeTimestamp()); // Garbage collection should only happen before/during appending new data, - // which should not happen in end-of-stream state. - DCHECK(!end_of_stream_); + // which should not happen in end-of-stream state. Unless we also allow GC to + // happen on memory pressure notifications, which might happen even in EOS + // state. + if (!base::FeatureList::IsEnabled(kMemoryPressureBasedSourceBufferGC)) + DCHECK(!end_of_stream_); // Compute size of |ranges_|. size_t ranges_size = GetBufferedSize(); @@ -713,11 +728,29 @@ return false; } + size_t effective_memory_limit = memory_limit_; + if (base::FeatureList::IsEnabled(kMemoryPressureBasedSourceBufferGC)) { + switch (memory_pressure_level_) { + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: + effective_memory_limit = memory_limit_ / 2; + break; + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: + effective_memory_limit = 0; + break; + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: + break; + } + } + // Return if we're under or at the memory limit. - if (ranges_size + newDataSize <= memory_limit_) + if (ranges_size + newDataSize <= effective_memory_limit) return true; - size_t bytes_to_free = ranges_size + newDataSize - memory_limit_; + size_t bytes_over_hard_memory_limit = 0; + if (ranges_size + newDataSize > memory_limit_) + bytes_over_hard_memory_limit = ranges_size + newDataSize - memory_limit_; + + size_t bytes_to_free = ranges_size + newDataSize - effective_memory_limit; DVLOG(2) << __func__ << " " << GetStreamTypeName() << ": Before GC media_time=" << media_time.InSecondsF() @@ -725,6 +758,7 @@ << " seek_pending_=" << seek_pending_ << " ranges_size=" << ranges_size << " newDataSize=" << newDataSize << " memory_limit_=" << memory_limit_ + << " effective_memory_limit=" << effective_memory_limit << " last_appended_buffer_timestamp_=" << last_appended_buffer_timestamp_.InSecondsF(); @@ -834,9 +868,10 @@ DVLOG(2) << __func__ << " " << GetStreamTypeName() << ": After GC bytes_to_free=" << bytes_to_free << " bytes_freed=" << bytes_freed + << " bytes_over_hard_memory_limit=" << bytes_over_hard_memory_limit << " ranges_=" << RangesToString(ranges_); - return bytes_freed >= bytes_to_free; + return bytes_freed >= bytes_over_hard_memory_limit; } size_t SourceBufferStream::FreeBuffersAfterLastAppended(
diff --git a/media/filters/source_buffer_stream.h b/media/filters/source_buffer_stream.h index 566477b..0a3f023 100644 --- a/media/filters/source_buffer_stream.h +++ b/media/filters/source_buffer_stream.h
@@ -19,6 +19,7 @@ #include <vector> #include "base/macros.h" +#include "base/memory/memory_pressure_listener.h" #include "base/memory/ref_counted.h" #include "media/base/audio_decoder_config.h" #include "media/base/media_export.h" @@ -93,6 +94,17 @@ bool GarbageCollectIfNeeded(DecodeTimestamp media_time, size_t newDataSize); + // Gets invoked when the system is experiencing memory pressure, i.e. there's + // not enough free memory. The |media_time| is the media playback position at + // the time of memory pressure notification (needed for accurate GC). The + // |memory_pressure_listener| indicates memory pressure severity. The + // |force_instant_gc| is used to force the MSE garbage collection algorithm to + // be run right away, without waiting for the next append. + void OnMemoryPressure( + DecodeTimestamp media_time, + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level, + bool force_instant_gc); + // Changes the SourceBufferStream's state so that it will start returning // buffers starting from the closest keyframe before |timestamp|. void Seek(base::TimeDelta timestamp); @@ -430,6 +442,9 @@ // Stores the largest distance between two adjacent buffers in this stream. base::TimeDelta max_interbuffer_distance_; + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level_ = + base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; + // The maximum amount of data in bytes the stream will keep in memory. size_t memory_limit_;
diff --git a/media/filters/source_buffer_stream_unittest.cc b/media/filters/source_buffer_stream_unittest.cc index c539839e1..d1e56ec 100644 --- a/media/filters/source_buffer_stream_unittest.cc +++ b/media/filters/source_buffer_stream_unittest.cc
@@ -16,9 +16,11 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" +#include "base/test/scoped_feature_list.h" #include "media/base/data_buffer.h" #include "media/base/decoder_buffer.h" #include "media/base/media_log.h" +#include "media/base/media_switches.h" #include "media/base/media_util.h" #include "media/base/mock_media_log.h" #include "media/base/test_helpers.h" @@ -171,6 +173,11 @@ stream_->Seek(base::TimeDelta::FromMilliseconds(timestamp_ms)); } + bool GarbageCollect(base::TimeDelta media_time, int new_data_size) { + return stream_->GarbageCollectIfNeeded( + DecodeTimestamp::FromPresentationTime(media_time), new_data_size); + } + bool GarbageCollectWithPlaybackAtBuffer(int position, int newDataBuffers) { return stream_->GarbageCollectIfNeeded( DecodeTimestamp::FromPresentationTime(position * frame_duration_), @@ -4752,6 +4759,77 @@ EXPECT_EQ(base::TimeDelta(), stream_->GetHighestPresentationTimestamp()); } +TEST_F(SourceBufferStreamTest, GarbageCollectionUnderMemoryPressure) { + SetMemoryLimit(16); + NewCodedFrameGroupAppend("0K 1 2 3K 4 5 6K 7 8 9K 10 11 12K 13 14 15K"); + CheckExpectedRangesByTimestamp("{ [0,16) }"); + + // This feature is disabled by default, so by default memory pressure + // notification takes no effect and the memory limits and won't remove + // anything from buffered ranges, since we are under the limit of 20 bytes. + stream_->OnMemoryPressure( + DecodeTimestamp::FromMilliseconds(0), + base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, false); + EXPECT_TRUE(GarbageCollect(base::TimeDelta::FromMilliseconds(8), 0)); + CheckExpectedRangesByTimestamp("{ [0,16) }"); + + // Now enable the feature. + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(kMemoryPressureBasedSourceBufferGC); + + // Verify that effective MSE memory limit is reduced under memory pressure. + stream_->OnMemoryPressure( + DecodeTimestamp::FromMilliseconds(0), + base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, false); + + // Effective memory limit is now 8 buffers, but we still will not collect any + // data between the current playback position 3 and last append position 15. + EXPECT_TRUE(GarbageCollect(base::TimeDelta::FromMilliseconds(4), 0)); + CheckExpectedRangesByTimestamp("{ [3,16) }"); + + // As playback proceeds further to time 9 we should be able to collect + // enough data to bring us back under memory limit of 8 buffers. + EXPECT_TRUE(GarbageCollect(base::TimeDelta::FromMilliseconds(9), 0)); + CheckExpectedRangesByTimestamp("{ [9,16) }"); + + // If memory pressure becomes critical, the garbage collection algorithm + // becomes even more aggressive and collects everything up to the current + // playback position. + stream_->OnMemoryPressure( + DecodeTimestamp::FromMilliseconds(0), + base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, false); + EXPECT_TRUE(GarbageCollect(base::TimeDelta::FromMilliseconds(13), 0)); + CheckExpectedRangesByTimestamp("{ [12,16) }"); + + // But even under critical memory pressure the MSE memory limit imposed by the + // memory pressure is soft, i.e. we should be able to append more data + // successfully up to the hard limit of 16 bytes. + NewCodedFrameGroupAppend("16K 17 18 19 20 21 22 23 24 25 26 27"); + CheckExpectedRangesByTimestamp("{ [12,28) }"); + EXPECT_TRUE(GarbageCollect(base::TimeDelta::FromMilliseconds(13), 0)); + CheckExpectedRangesByTimestamp("{ [12,28) }"); +} + +TEST_F(SourceBufferStreamTest, InstantGarbageCollectionUnderMemoryPressure) { + SetMemoryLimit(16); + NewCodedFrameGroupAppend("0K 1 2 3K 4 5 6K 7 8 9K 10 11 12K 13 14 15K"); + CheckExpectedRangesByTimestamp("{ [0,16) }"); + + // Verify that garbage collection happens immediately on critical memory + // pressure notification, even without explicit GarbageCollect invocation, + // when the immediate GC is allowed. + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(kMemoryPressureBasedSourceBufferGC); + stream_->OnMemoryPressure( + DecodeTimestamp::FromMilliseconds(7), + base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, true); + CheckExpectedRangesByTimestamp("{ [6,16) }"); + stream_->OnMemoryPressure( + DecodeTimestamp::FromMilliseconds(9), + base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, true); + CheckExpectedRangesByTimestamp("{ [9,16) }"); +} + // TODO(vrk): Add unit tests where keyframes are unaligned between streams. // (crbug.com/133557)
diff --git a/media/gpu/android_video_decode_accelerator.cc b/media/gpu/android_video_decode_accelerator.cc index 3d0e4cf..a6a1b95 100644 --- a/media/gpu/android_video_decode_accelerator.cc +++ b/media/gpu/android_video_decode_accelerator.cc
@@ -234,6 +234,7 @@ media_drm_bridge_cdm_context_(nullptr), cdm_registration_id_(0), pending_input_buf_index_(-1), + during_initialize_(false), deferred_initialization_pending_(false), codec_needs_reset_(false), defer_surface_creation_(false), @@ -265,6 +266,7 @@ TRACE_EVENT0("media", "AVDA::Initialize"); DCHECK(!media_codec_); DCHECK(thread_checker_.CalledOnValidThread()); + base::AutoReset<bool> scoper(&during_initialize_, true); if (make_context_current_cb_.is_null() || get_gles2_decoder_cb_.is_null()) { DLOG(ERROR) << "GL callbacks are required for this VDA"; @@ -319,17 +321,6 @@ // surface ID from the codec configuration. DCHECK(!pending_surface_id_); - // If we're low on resources, we may decide to defer creation of the surface - // until the codec is actually used. - if (ShouldDeferSurfaceCreation(config_.surface_id, codec_config_->codec)) { - DCHECK(!deferred_initialization_pending_); - // We should never be here if a SurfaceView is required. - DCHECK_EQ(config_.surface_id, SurfaceManager::kNoSurfaceID); - defer_surface_creation_ = true; - NotifyInitializationComplete(true); - return true; - } - // We signaled that we support deferred initialization, so see if the client // does also. deferred_initialization_pending_ = config.is_deferred_initialization_allowed; @@ -338,14 +329,30 @@ return false; } + // If we're low on resources, we may decide to defer creation of the surface + // until the codec is actually used. + if (ShouldDeferSurfaceCreation(config_.surface_id, codec_config_->codec)) { + // We should never be here if a SurfaceView is required. + DCHECK_EQ(config_.surface_id, SurfaceManager::kNoSurfaceID); + defer_surface_creation_ = true; + if (deferred_initialization_pending_) + NotifyInitializationSucceeded(); + return true; + } + if (AVDACodecAllocator::Instance()->AllocateSurface(this, config_.surface_id)) { - // We now own the surface, so finish initialization. - return InitializePictureBufferManager(); + // We now own the surface, so continue initialization. + InitializePictureBufferManager(); + return state_ != ERROR; } // We have to wait for some other AVDA instance to free up the surface. - // OnSurfaceAvailable will be called when it's available. + // OnSurfaceAvailable will be called when it's available. Note that if we're + // deferring initialization, then this will cause the client to wait for the + // result. If we're not, then the client will assume success even though + // we don't technically know if we will get a surface and codec yet. + DCHECK_EQ(state_, NO_ERROR); return true; } @@ -353,37 +360,44 @@ DCHECK(deferred_initialization_pending_); DCHECK(!defer_surface_creation_); - if (!success || !InitializePictureBufferManager()) { - NotifyInitializationComplete(false); - deferred_initialization_pending_ = false; + if (!success) { + NOTIFY_ERROR(PLATFORM_FAILURE, "Surface is not available"); + return; } + + InitializePictureBufferManager(); } -bool AndroidVideoDecodeAccelerator::InitializePictureBufferManager() { +void AndroidVideoDecodeAccelerator::InitializePictureBufferManager() { if (!make_context_current_cb_.Run()) { - LOG(ERROR) << "Failed to make this decoder's GL context current."; - return false; + NOTIFY_ERROR(PLATFORM_FAILURE, + "Failed to make this decoder's GL context current"); + return; } codec_config_->surface = picture_buffer_manager_.Initialize(config_.surface_id); codec_config_->surface_texture = picture_buffer_manager_.surface_texture(); - if (codec_config_->surface.IsEmpty()) - return false; + if (codec_config_->surface.IsEmpty()) { + NOTIFY_ERROR(PLATFORM_FAILURE, "Codec surface is empty"); + return; + } - if (!AVDACodecAllocator::Instance()->StartThread(this)) - return false; + if (!AVDACodecAllocator::Instance()->StartThread(this)) { + NOTIFY_ERROR(PLATFORM_FAILURE, "Unable to start thread"); + return; + } // If we are encrypted, then we aren't able to create the codec yet. if (config_.is_encrypted()) { InitializeCdm(); - return true; + return; } if (deferred_initialization_pending_ || defer_surface_creation_) { defer_surface_creation_ = false; ConfigureMediaCodecAsynchronously(); - return true; + return; } // If the client doesn't support deferred initialization (WebRTC), then we @@ -392,7 +406,7 @@ // all (::Initialize or the wrapper can do it), but then they have to remember // not to start codec config if we have to wait for the cdm. It's somewhat // clearer for us to handle both cases. - return ConfigureMediaCodecSynchronously(); + ConfigureMediaCodecSynchronously(); } void AndroidVideoDecodeAccelerator::DoIOTask(bool start_timer) { @@ -764,10 +778,12 @@ const BitstreamBuffer& bitstream_buffer) { DCHECK(thread_checker_.CalledOnValidThread()); - if (defer_surface_creation_ && !InitializePictureBufferManager()) { - NOTIFY_ERROR(PLATFORM_FAILURE, - "Failed deferred surface and MediaCodec initialization."); - return; + if (defer_surface_creation_) { + InitializePictureBufferManager(); + if (state_ == ERROR) { + DLOG(ERROR) << "Failed deferred surface and MediaCodec initialization."; + return; + } } // If we previously deferred a codec restart, take care of it now. This can @@ -898,7 +914,7 @@ weak_this_factory_.GetWeakPtr(), codec_config_); } -bool AndroidVideoDecodeAccelerator::ConfigureMediaCodecSynchronously() { +void AndroidVideoDecodeAccelerator::ConfigureMediaCodecSynchronously() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!media_codec_); DCHECK_NE(state_, WAITING_FOR_CODEC); @@ -909,14 +925,14 @@ if (!task_type) { // If there is no free thread, then just fail. OnCodecConfigured(nullptr); - return false; + return; } codec_config_->task_type = task_type.value(); std::unique_ptr<VideoCodecBridge> media_codec = AVDACodecAllocator::Instance()->CreateMediaCodecSync(codec_config_); + // Note that |media_codec| might be null, which will NotifyError. OnCodecConfigured(std::move(media_codec)); - return !!media_codec_; } void AndroidVideoDecodeAccelerator::OnCodecConfigured( @@ -925,19 +941,18 @@ DCHECK(state_ == WAITING_FOR_CODEC || state_ == SURFACE_DESTROYED); // If we are supposed to notify that initialization is complete, then do so - // now. Otherwise, this is a reconfiguration. - if (deferred_initialization_pending_) { - // Losing the output surface is not considered an error state, so notify - // success. The client will destroy this soon. - NotifyInitializationComplete(state_ == SURFACE_DESTROYED ? true - : !!media_codec); - deferred_initialization_pending_ = false; - } + // before returning. Otherwise, this is a reconfiguration. // If |state_| changed to SURFACE_DESTROYED while we were configuring a codec, // then the codec is already invalid so we return early and drop it. - if (state_ == SURFACE_DESTROYED) + if (state_ == SURFACE_DESTROYED) { + if (deferred_initialization_pending_) { + // Losing the output surface is not considered an error state, so notify + // success. The client will destroy this soon. + NotifyInitializationSucceeded(); + } return; + } DCHECK(!media_codec_); media_codec_ = std::move(media_codec); @@ -947,6 +962,9 @@ return; } + if (deferred_initialization_pending_) + NotifyInitializationSucceeded(); + state_ = NO_ERROR; ManageTimer(true); @@ -1220,7 +1238,7 @@ #if !defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) NOTIMPLEMENTED(); - NotifyInitializationComplete(false); + NOTIFY_ERROR(PLATFORM_FAILURE, "Cdm support needs mojo in the gpu process"); #else // Store the CDM to hold a reference to it. cdm_for_reference_holding_only_ = @@ -1261,7 +1279,7 @@ LOG(ERROR) << "MediaCrypto is not available, can't play encrypted stream."; cdm_for_reference_holding_only_ = nullptr; media_drm_bridge_cdm_context_ = nullptr; - NotifyInitializationComplete(false); + NOTIFY_ERROR(PLATFORM_FAILURE, "MediaCrypto is not available"); return; } @@ -1287,9 +1305,12 @@ DoIOTask(true); } -void AndroidVideoDecodeAccelerator::NotifyInitializationComplete(bool success) { +void AndroidVideoDecodeAccelerator::NotifyInitializationSucceeded() { + DCHECK(deferred_initialization_pending_); + if (client_) - client_->NotifyInitializationComplete(success); + client_->NotifyInitializationComplete(true); + deferred_initialization_pending_ = false; } void AndroidVideoDecodeAccelerator::NotifyPictureReady(const Picture& picture) { @@ -1315,6 +1336,20 @@ void AndroidVideoDecodeAccelerator::NotifyError(Error error) { state_ = ERROR; + + // If we're in the middle of Initialize, then stop. It will notice |state_|. + if (during_initialize_) + return; + + // If deferred init is pending, then notify the client that it failed. + if (deferred_initialization_pending_) { + if (client_) + client_->NotifyInitializationComplete(false); + deferred_initialization_pending_ = false; + return; + } + + // We're after all init. Just signal an error. if (client_) client_->NotifyError(error); }
diff --git a/media/gpu/android_video_decode_accelerator.h b/media/gpu/android_video_decode_accelerator.h index 172d2dce..4d903692 100644 --- a/media/gpu/android_video_decode_accelerator.h +++ b/media/gpu/android_video_decode_accelerator.h
@@ -68,7 +68,10 @@ // AVDAStateProvider implementation: const gfx::Size& GetSize() const override; base::WeakPtr<gpu::gles2::GLES2Decoder> GetGlDecoder() const override; - // Notifies the client about the error and sets |state_| to |ERROR|. + // Notifies the client about the error and sets |state_| to |ERROR|. If we're + // in the middle of Initialize, we guarantee that Initialize will return + // failure. If deferred init is pending, then we'll fail deferred init. + // Otherwise, we'll signal errors normally. void NotifyError(Error error) override; // AVDACodecAllocatorClient implementation: @@ -101,11 +104,11 @@ }; // Initialize of the picture buffer manager. This is to be called when the - // SurfaceView in |surface_id_|, if any, is no longer busy. It will return - // false on failure, and true if initialization was successful. This includes - // synchronous and asynchronous init; the AVDA might not yet have a codec on - // success, but async init will at least be in progress. - bool InitializePictureBufferManager(); + // SurfaceView in |surface_id_|, if any, is no longer busy. On failure, it + // will set |state_| to ERROR. On success, it will proceed to either sync or + // async codec config. Note that we might not actually have a codec when this + // returns, however. + void InitializePictureBufferManager(); // A part of destruction process that is sometimes postponed after the drain. void ActualDestroy(); @@ -118,11 +121,10 @@ // |state_| is no longer WAITING_FOR_CODEC. void ConfigureMediaCodecAsynchronously(); - // Like ConfigureMediaCodecAsynchronously, but synchronous. Returns true if - // and only if |media_codec_| is non-null. Since all configuration is done - // synchronously, there is no concern with modifying |codec_config_| after - // this returns. - bool ConfigureMediaCodecSynchronously(); + // Like ConfigureMediaCodecAsynchronously, but synchronous. Will NotifyError + // on failure. Since all configuration is done synchronously, there is no + // concern with modifying |codec_config_| after this returns. + void ConfigureMediaCodecSynchronously(); // Instantiate a media codec using |codec_config|. // This may be called on any thread. @@ -164,8 +166,9 @@ // Called when a new key is added to the CDM. void OnKeyAdded(); - // Notifies the client of the result of deferred initialization. - void NotifyInitializationComplete(bool success); + // Notifies the client that deferred initialization succeeded. If it fails, + // then call NotifyError instead. + void NotifyInitializationSucceeded(); // Notifies the client about the availability of a picture. void NotifyPictureReady(const Picture& picture); @@ -309,6 +312,10 @@ // from being sent after a reset. int error_sequence_token_; + // Are we currently processing a call to Initialize()? Please don't use this + // unless you're NotifyError. + bool during_initialize_; + // True if and only if VDA initialization is deferred, and we have not yet // called NotifyInitializationComplete. bool deferred_initialization_pending_;
diff --git a/media/mojo/clients/mojo_renderer.cc b/media/mojo/clients/mojo_renderer.cc index 119de624..6d284985 100644 --- a/media/mojo/clients/mojo_renderer.cc +++ b/media/mojo/clients/mojo_renderer.cc
@@ -78,28 +78,33 @@ DemuxerStream* const video = demuxer_stream_provider_->GetStream(DemuxerStream::VIDEO); - mojom::DemuxerStreamPtr audio_stream; + std::vector<mojom::DemuxerStreamPtr> streams; if (audio) { - audio_stream_.reset( - new MojoDemuxerStreamImpl(audio, MakeRequest(&audio_stream))); - // Using base::Unretained(this) is safe because |this| owns - // |audio_stream_|, and the error handler can't be invoked once - // |audio_stream_| is destroyed. - audio_stream_->set_connection_error_handler( + mojom::DemuxerStreamPtr audio_stream; + std::unique_ptr<MojoDemuxerStreamImpl> mojo_stream = + base::MakeUnique<MojoDemuxerStreamImpl>(audio, + MakeRequest(&audio_stream)); + // Using base::Unretained(this) is safe because |this| owns |mojo_stream|, + // and the error handler can't be invoked once |mojo_stream| is destroyed. + mojo_stream->set_connection_error_handler( base::Bind(&MojoRenderer::OnDemuxerStreamConnectionError, - base::Unretained(this), DemuxerStream::AUDIO)); + base::Unretained(this), mojo_stream.get())); + streams_.push_back(std::move(mojo_stream)); + streams.push_back(std::move(audio_stream)); } - mojom::DemuxerStreamPtr video_stream; if (video) { - video_stream_.reset( - new MojoDemuxerStreamImpl(video, MakeRequest(&video_stream))); - // Using base::Unretained(this) is safe because |this| owns - // |video_stream_|, and the error handler can't be invoked once - // |video_stream_| is destroyed. - video_stream_->set_connection_error_handler( + mojom::DemuxerStreamPtr video_stream; + std::unique_ptr<MojoDemuxerStreamImpl> mojo_stream = + base::MakeUnique<MojoDemuxerStreamImpl>(video, + MakeRequest(&video_stream)); + // Using base::Unretained(this) is safe because |this| owns |mojo_stream|, + // and the error handler can't be invoked once |mojo_stream| is destroyed. + mojo_stream->set_connection_error_handler( base::Bind(&MojoRenderer::OnDemuxerStreamConnectionError, - base::Unretained(this), DemuxerStream::VIDEO)); + base::Unretained(this), mojo_stream.get())); + streams_.push_back(std::move(mojo_stream)); + streams.push_back(std::move(video_stream)); } BindRemoteRendererIfNeeded(); @@ -111,8 +116,8 @@ // |remote_renderer_|, and the callback won't be dispatched if // |remote_renderer_| is destroyed. remote_renderer_->Initialize( - std::move(client_ptr_info), std::move(audio_stream), - std::move(video_stream), base::nullopt, base::nullopt, + std::move(client_ptr_info), std::move(streams), base::nullopt, + base::nullopt, base::Bind(&MojoRenderer::OnInitialized, base::Unretained(this), client)); } @@ -130,9 +135,9 @@ // Using base::Unretained(this) is safe because |this| owns // |remote_renderer_|, and the callback won't be dispatched if // |remote_renderer_| is destroyed. + std::vector<mojom::DemuxerStreamPtr> streams; remote_renderer_->Initialize( - std::move(client_ptr_info), mojom::DemuxerStreamPtr(), - mojom::DemuxerStreamPtr(), url_params.media_url, + std::move(client_ptr_info), std::move(streams), url_params.media_url, url_params.first_party_for_cookies, base::Bind(&MojoRenderer::OnInitialized, base::Unretained(this), client)); } @@ -315,17 +320,18 @@ client_->OnError(PIPELINE_ERROR_DECODE); } -void MojoRenderer::OnDemuxerStreamConnectionError(DemuxerStream::Type type) { - DVLOG(1) << __func__ << ": " << type; +void MojoRenderer::OnDemuxerStreamConnectionError( + MojoDemuxerStreamImpl* stream) { + DVLOG(1) << __func__ << ": stream=" << stream; DCHECK(task_runner_->BelongsToCurrentThread()); - if (type == DemuxerStream::AUDIO) { - audio_stream_.reset(); - } else if (type == DemuxerStream::VIDEO) { - video_stream_.reset(); - } else { - NOTREACHED() << "Unexpected demuxer stream type: " << type; + for (auto& s : streams_) { + if (s.get() == stream) { + s.reset(); + return; + } } + NOTREACHED() << "Unrecognized demuxer stream=" << stream; } void MojoRenderer::BindRemoteRendererIfNeeded() {
diff --git a/media/mojo/clients/mojo_renderer.h b/media/mojo/clients/mojo_renderer.h index 995a0763..6535321 100644 --- a/media/mojo/clients/mojo_renderer.h +++ b/media/mojo/clients/mojo_renderer.h
@@ -7,6 +7,9 @@ #include <stdint.h> +#include <memory> +#include <vector> + #include "base/macros.h" #include "base/time/default_tick_clock.h" #include "base/unguessable_token.h" @@ -96,8 +99,9 @@ // Callback for connection error on |remote_renderer_|. void OnConnectionError(); - // Callback for connection error on |audio_stream_| and |video_stream_|. - void OnDemuxerStreamConnectionError(DemuxerStream::Type type); + // Callback for connection error on any of |streams_|. The |stream| parameter + // indicates which stream the error happened on. + void OnDemuxerStreamConnectionError(MojoDemuxerStreamImpl* stream); // Callbacks for |remote_renderer_| methods. void OnInitialized(media::RendererClient* client, bool success); @@ -131,8 +135,7 @@ // destroyed. The local demuxer streams returned by DemuxerStreamProvider // cannot be used after |this| is destroyed. // TODO(alokp): Add tests for MojoDemuxerStreamImpl. - std::unique_ptr<MojoDemuxerStreamImpl> audio_stream_; - std::unique_ptr<MojoDemuxerStreamImpl> video_stream_; + std::vector<std::unique_ptr<MojoDemuxerStreamImpl>> streams_; // This class is constructed on one thread and used exclusively on another // thread. This member is used to safely pass the RendererPtr from one thread
diff --git a/media/mojo/interfaces/renderer.mojom b/media/mojo/interfaces/renderer.mojom index 38ca440..0342f04 100644 --- a/media/mojo/interfaces/renderer.mojom +++ b/media/mojo/interfaces/renderer.mojom
@@ -12,11 +12,11 @@ import "url/mojo/url.mojom"; interface Renderer { - // Initializes the Renderer with one or both of an audio and video stream, - // executing the callback with whether the initialization succeeded. + // Initializes the Renderer with either one or more audio / video |streams|, + // or a |media_url| executing the callback with whether the initialization + // succeeded. Initialize(associated RendererClient client, - DemuxerStream? audio, - DemuxerStream? video, + array<DemuxerStream>? streams, url.mojom.Url? media_url, url.mojom.Url? first_party_for_cookies) => (bool success);
diff --git a/media/mojo/services/demuxer_stream_provider_shim.cc b/media/mojo/services/demuxer_stream_provider_shim.cc index 0c9d8fe..1bb7e602 100644 --- a/media/mojo/services/demuxer_stream_provider_shim.cc +++ b/media/mojo/services/demuxer_stream_provider_shim.cc
@@ -13,36 +13,31 @@ namespace media { DemuxerStreamProviderShim::DemuxerStreamProviderShim( - mojom::DemuxerStreamPtr audio, - mojom::DemuxerStreamPtr video, + std::vector<mojom::DemuxerStreamPtr> streams, const base::Closure& demuxer_ready_cb) : demuxer_ready_cb_(demuxer_ready_cb), streams_ready_(0), weak_factory_(this) { - DCHECK(audio || video); + DCHECK(!streams.empty()); DCHECK(!demuxer_ready_cb_.is_null()); - if (audio) { - streams_.push_back(new MojoDemuxerStreamAdapter( - std::move(audio), base::Bind(&DemuxerStreamProviderShim::OnStreamReady, - weak_factory_.GetWeakPtr()))); - } - - if (video) { - streams_.push_back(new MojoDemuxerStreamAdapter( - std::move(video), base::Bind(&DemuxerStreamProviderShim::OnStreamReady, - weak_factory_.GetWeakPtr()))); + for (auto& s : streams) { + streams_.emplace_back(new MojoDemuxerStreamAdapter( + std::move(s), base::Bind(&DemuxerStreamProviderShim::OnStreamReady, + weak_factory_.GetWeakPtr()))); } } DemuxerStreamProviderShim::~DemuxerStreamProviderShim() { } +// This function returns only the first stream of the given |type| for now. +// TODO(servolk): Make this work with multiple streams. DemuxerStream* DemuxerStreamProviderShim::GetStream(DemuxerStream::Type type) { DCHECK(demuxer_ready_cb_.is_null()); - for (auto* stream : streams_) { + for (auto& stream : streams_) { if (stream->type() == type) - return stream; + return stream.get(); } return nullptr;
diff --git a/media/mojo/services/demuxer_stream_provider_shim.h b/media/mojo/services/demuxer_stream_provider_shim.h index b12b2a5..b1b6da9 100644 --- a/media/mojo/services/demuxer_stream_provider_shim.h +++ b/media/mojo/services/demuxer_stream_provider_shim.h
@@ -9,7 +9,6 @@ #include "base/callback.h" #include "base/macros.h" -#include "base/memory/scoped_vector.h" #include "base/memory/weak_ptr.h" #include "media/base/demuxer_stream_provider.h" #include "media/mojo/services/mojo_demuxer_stream_adapter.h" @@ -22,8 +21,7 @@ // Constructs the shim; at least a single audio or video stream must be // provided. |demuxer_ready_cb| will be called once the streams have been // initialized. Calling any method before then is an error. - DemuxerStreamProviderShim(mojom::DemuxerStreamPtr audio, - mojom::DemuxerStreamPtr video, + DemuxerStreamProviderShim(std::vector<mojom::DemuxerStreamPtr> streams, const base::Closure& demuxer_ready_cb); ~DemuxerStreamProviderShim() override; @@ -40,11 +38,10 @@ // all streams are ready. base::Closure demuxer_ready_cb_; - // Scoped container for demuxer stream adapters which interface with the - // mojo level demuxer streams. |streams_ready_| tracks how many streams are - // ready and is used by OnStreamReady() to know when |demuxer_ready_cb_| - // should be fired. - ScopedVector<MojoDemuxerStreamAdapter> streams_; + // Container for demuxer stream adapters which interface with the mojo level + // demuxer streams. |streams_ready_| tracks how many streams are ready and is + // used by OnStreamReady() to know when |demuxer_ready_cb_| should be fired. + std::vector<std::unique_ptr<MojoDemuxerStreamAdapter>> streams_; size_t streams_ready_; // WeakPtrFactorys must always be the last member variable.
diff --git a/media/mojo/services/media_service_unittest.cc b/media/mojo/services/media_service_unittest.cc index ee07938..c5f9ef5 100644 --- a/media/mojo/services/media_service_unittest.cc +++ b/media/mojo/services/media_service_unittest.cc
@@ -138,9 +138,10 @@ EXPECT_CALL(*this, OnRendererInitialized(expected_result)) .Times(Exactly(1)) .WillOnce(InvokeWithoutArgs(run_loop_.get(), &base::RunLoop::Quit)); - renderer_->Initialize(std::move(client_ptr_info), nullptr, - std::move(video_stream_proxy), base::nullopt, - base::nullopt, + std::vector<mojom::DemuxerStreamPtr> streams; + streams.push_back(std::move(video_stream_proxy)); + renderer_->Initialize(std::move(client_ptr_info), std::move(streams), + base::nullopt, base::nullopt, base::Bind(&MediaServiceTest::OnRendererInitialized, base::Unretained(this))); }
diff --git a/media/mojo/services/mojo_renderer_service.cc b/media/mojo/services/mojo_renderer_service.cc index 71b6019..e81f546 100644 --- a/media/mojo/services/mojo_renderer_service.cc +++ b/media/mojo/services/mojo_renderer_service.cc
@@ -77,8 +77,7 @@ void MojoRendererService::Initialize( mojom::RendererClientAssociatedPtrInfo client, - mojom::DemuxerStreamPtr audio, - mojom::DemuxerStreamPtr video, + base::Optional<std::vector<mojom::DemuxerStreamPtr>> streams, const base::Optional<GURL>& media_url, const base::Optional<GURL>& first_party_for_cookies, const InitializeCallback& callback) { @@ -89,14 +88,13 @@ state_ = STATE_INITIALIZING; if (media_url == base::nullopt) { + DCHECK(streams.has_value()); stream_provider_.reset(new DemuxerStreamProviderShim( - std::move(audio), std::move(video), + std::move(*streams), base::Bind(&MojoRendererService::OnStreamReady, weak_this_, callback))); return; } - DCHECK(!audio); - DCHECK(!video); DCHECK(!media_url.value().is_empty()); DCHECK(first_party_for_cookies); stream_provider_.reset(new MediaUrlDemuxer(nullptr, media_url.value(),
diff --git a/media/mojo/services/mojo_renderer_service.h b/media/mojo/services/mojo_renderer_service.h index c088d59..d9bd5b6 100644 --- a/media/mojo/services/mojo_renderer_service.h +++ b/media/mojo/services/mojo_renderer_service.h
@@ -63,8 +63,7 @@ // mojom::Renderer implementation. void Initialize(mojom::RendererClientAssociatedPtrInfo client, - mojom::DemuxerStreamPtr audio, - mojom::DemuxerStreamPtr video, + base::Optional<std::vector<mojom::DemuxerStreamPtr>> streams, const base::Optional<GURL>& media_url, const base::Optional<GURL>& first_party_for_cookies, const InitializeCallback& callback) final;
diff --git a/mojo/public/cpp/bindings/associated_binding.h b/mojo/public/cpp/bindings/associated_binding.h index 0a95da4..f7787d7f 100644 --- a/mojo/public/cpp/bindings/associated_binding.h +++ b/mojo/public/cpp/bindings/associated_binding.h
@@ -17,7 +17,6 @@ #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "mojo/public/cpp/bindings/associated_group.h" -#include "mojo/public/cpp/bindings/associated_group_controller.h" #include "mojo/public/cpp/bindings/associated_interface_request.h" #include "mojo/public/cpp/bindings/connection_error_callback.h" #include "mojo/public/cpp/bindings/interface_endpoint_client.h" @@ -109,11 +108,6 @@ std::move(handle), &stub_, base::WrapUnique(new typename Interface::RequestValidator_()), Interface::HasSyncMethods_, std::move(runner), Interface::Version_)); - - if (Interface::PassesAssociatedKinds_) { - stub_.serialization_context()->group_controller = - endpoint_client_->group_controller(); - } } // Adds a message filter to be notified of each incoming message before
diff --git a/mojo/public/cpp/bindings/associated_group_controller.h b/mojo/public/cpp/bindings/associated_group_controller.h index 6ca94f9..e42a889 100644 --- a/mojo/public/cpp/bindings/associated_group_controller.h +++ b/mojo/public/cpp/bindings/associated_group_controller.h
@@ -38,6 +38,9 @@ // Typically, this method is used to (1) create an endpoint handle for the // master interface; or (2) create an endpoint handle on receiving an // interface ID from the message pipe. + // + // On failure, the method returns an invalid handle. Usually that is because + // the ID has already been used to create a handle. virtual ScopedInterfaceEndpointHandle CreateLocalEndpointHandle( InterfaceId id) = 0;
diff --git a/mojo/public/cpp/bindings/lib/array_internal.h b/mojo/public/cpp/bindings/lib/array_internal.h index 9bb3521..eecfcfbc 100644 --- a/mojo/public/cpp/bindings/lib/array_internal.h +++ b/mojo/public/cpp/bindings/lib/array_internal.h
@@ -263,7 +263,7 @@ T, IsUnionDataType<T>::value, std::is_same<T, AssociatedInterface_Data>::value || - std::is_same<T, AssociatedInterfaceRequest_Data>::value || + std::is_same<T, AssociatedEndpointHandle_Data>::value || std::is_same<T, Interface_Data>::value || std::is_same<T, Handle_Data>::value>; using Element = T;
diff --git a/mojo/public/cpp/bindings/lib/array_serialization.h b/mojo/public/cpp/bindings/lib/array_serialization.h index acfd4e3e..d2f8ecfd72 100644 --- a/mojo/public/cpp/bindings/lib/array_serialization.h +++ b/mojo/public/cpp/bindings/lib/array_serialization.h
@@ -288,8 +288,17 @@ static size_t GetSerializedSize(UserTypeIterator* input, SerializationContext* context) { - return sizeof(Data) + - Align(input->GetSize() * sizeof(typename Data::Element)); + size_t element_count = input->GetSize(); + if (BelongsTo<Element, + MojomTypeCategory::ASSOCIATED_INTERFACE | + MojomTypeCategory::ASSOCIATED_INTERFACE_REQUEST>::value) { + for (size_t i = 0; i < element_count; ++i) { + typename UserTypeIterator::GetNextResult next = input->GetNext(); + size_t size = PrepareToSerialize<Element>(next, context); + DCHECK_EQ(size, 0u); + } + } + return sizeof(Data) + Align(element_count * sizeof(typename Data::Element)); } static void SerializeElements(UserTypeIterator* input,
diff --git a/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h b/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h index 9e70d92..f6a7777c 100644 --- a/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h +++ b/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h
@@ -19,7 +19,6 @@ #include "base/memory/ref_counted.h" #include "base/single_thread_task_runner.h" #include "mojo/public/cpp/bindings/associated_group.h" -#include "mojo/public/cpp/bindings/associated_group_controller.h" #include "mojo/public/cpp/bindings/associated_interface_ptr_info.h" #include "mojo/public/cpp/bindings/connection_error_callback.h" #include "mojo/public/cpp/bindings/interface_endpoint_client.h" @@ -100,9 +99,6 @@ base::WrapUnique(new typename Interface::ResponseValidator_()), false, std::move(runner), 0u)); proxy_.reset(new Proxy(endpoint_client_.get())); - if (Interface::PassesAssociatedKinds_) { - proxy_->set_group_controller(endpoint_client_->group_controller()); - } } // After this method is called, the object is in an invalid state and
diff --git a/mojo/public/cpp/bindings/lib/binding_state.h b/mojo/public/cpp/bindings/lib/binding_state.h index a57d904..209b3ad 100644 --- a/mojo/public/cpp/bindings/lib/binding_state.h +++ b/mojo/public/cpp/bindings/lib/binding_state.h
@@ -109,8 +109,6 @@ base::MakeUnique<typename Interface::RequestValidator_>(), Interface::PassesAssociatedKinds_, Interface::HasSyncMethods_, &stub_, Interface::Version_); - if (Interface::PassesAssociatedKinds_) - stub_.serialization_context()->group_controller = router_; } InterfaceRequest<Interface> Unbind() {
diff --git a/mojo/public/cpp/bindings/lib/bindings_internal.h b/mojo/public/cpp/bindings/lib/bindings_internal.h index 494abe3f..631daec 100644 --- a/mojo/public/cpp/bindings/lib/bindings_internal.h +++ b/mojo/public/cpp/bindings/lib/bindings_internal.h
@@ -124,6 +124,8 @@ }; static_assert(sizeof(Pointer<char>) == 8, "Bad_sizeof(Pointer)"); +using GenericPointer = Pointer<void>; + struct Handle_Data { Handle_Data() = default; explicit Handle_Data(uint32_t value) : value(value) {} @@ -140,19 +142,24 @@ }; static_assert(sizeof(Interface_Data) == 8, "Bad_sizeof(Interface_Data)"); +struct AssociatedEndpointHandle_Data { + AssociatedEndpointHandle_Data() = default; + explicit AssociatedEndpointHandle_Data(uint32_t value) : value(value) {} + + bool is_valid() const { return value != kEncodedInvalidHandleValue; } + + uint32_t value; +}; +static_assert(sizeof(AssociatedEndpointHandle_Data) == 4, + "Bad_sizeof(AssociatedEndpointHandle_Data)"); + struct AssociatedInterface_Data { - InterfaceId interface_id; + AssociatedEndpointHandle_Data handle; uint32_t version; }; static_assert(sizeof(AssociatedInterface_Data) == 8, "Bad_sizeof(AssociatedInterface_Data)"); -struct AssociatedInterfaceRequest_Data { - InterfaceId interface_id; -}; -static_assert(sizeof(AssociatedInterfaceRequest_Data) == 4, - "Bad_sizeof(AssociatedInterfaceRequest_Data)"); - #pragma pack(pop) template <typename T> @@ -234,7 +241,7 @@ template <typename T> struct MojomTypeTraits<AssociatedInterfaceRequestDataView<T>, false> { - using Data = AssociatedInterfaceRequest_Data; + using Data = AssociatedEndpointHandle_Data; using DataAsArrayElement = Data; static const MojomTypeCategory category =
diff --git a/mojo/public/cpp/bindings/lib/control_message_handler.cc b/mojo/public/cpp/bindings/lib/control_message_handler.cc index b1124894..c90aadad 100644 --- a/mojo/public/cpp/bindings/lib/control_message_handler.cc +++ b/mojo/public/cpp/bindings/lib/control_message_handler.cc
@@ -19,9 +19,9 @@ namespace { bool ValidateControlRequestWithResponse(Message* message) { - ValidationContext validation_context( - message->data(), message->data_num_bytes(), message->handles()->size(), - message, "ControlRequestValidator"); + ValidationContext validation_context(message->payload(), + message->payload_num_bytes(), 0, 0, + message, "ControlRequestValidator"); if (!ValidateMessageIsRequestExpectingResponse(message, &validation_context)) return false; @@ -35,9 +35,9 @@ } bool ValidateControlRequestWithoutResponse(Message* message) { - ValidationContext validation_context( - message->data(), message->data_num_bytes(), message->handles()->size(), - message, "ControlRequestValidator"); + ValidationContext validation_context(message->payload(), + message->payload_num_bytes(), 0, 0, + message, "ControlRequestValidator"); if (!ValidateMessageIsRequestWithoutResponse(message, &validation_context)) return false; @@ -116,8 +116,9 @@ size_t size = PrepareToSerialize<interface_control::RunResponseMessageParamsDataView>( response_params_ptr, &context_); - ResponseMessageBuilder builder(interface_control::kRunMessageId, size, - message->request_id()); + MessageBuilder builder(interface_control::kRunMessageId, + Message::kFlagIsResponse, size, 0); + builder.message()->set_request_id(message->request_id()); interface_control::internal::RunResponseMessageParams_Data* response_params = nullptr;
diff --git a/mojo/public/cpp/bindings/lib/control_message_proxy.cc b/mojo/public/cpp/bindings/lib/control_message_proxy.cc index 44db40c2..23de991 100644 --- a/mojo/public/cpp/bindings/lib/control_message_proxy.cc +++ b/mojo/public/cpp/bindings/lib/control_message_proxy.cc
@@ -24,9 +24,9 @@ namespace { bool ValidateControlResponse(Message* message) { - ValidationContext validation_context( - message->data(), message->data_num_bytes(), message->handles()->size(), - message, "ControlResponseValidator"); + ValidationContext validation_context(message->payload(), + message->payload_num_bytes(), 0, 0, + message, "ControlResponseValidator"); if (!ValidateMessageIsResponse(message, &validation_context)) return false; @@ -79,7 +79,8 @@ params_ptr->input = std::move(input_ptr); size_t size = PrepareToSerialize<interface_control::RunMessageParamsDataView>( params_ptr, &context); - RequestMessageBuilder builder(interface_control::kRunMessageId, size); + MessageBuilder builder(interface_control::kRunMessageId, + Message::kFlagExpectsResponse, size, 0); interface_control::internal::RunMessageParams_Data* params = nullptr; Serialize<interface_control::RunMessageParamsDataView>( @@ -99,7 +100,8 @@ size_t size = PrepareToSerialize< interface_control::RunOrClosePipeMessageParamsDataView>(params_ptr, &context); - MessageBuilder builder(interface_control::kRunOrClosePipeMessageId, size); + MessageBuilder builder(interface_control::kRunOrClosePipeMessageId, 0, size, + 0); interface_control::internal::RunOrClosePipeMessageParams_Data* params = nullptr;
diff --git a/mojo/public/cpp/bindings/lib/handle_interface_serialization.h b/mojo/public/cpp/bindings/lib/handle_interface_serialization.h index 8b26d84b..c7b3cb2 100644 --- a/mojo/public/cpp/bindings/lib/handle_interface_serialization.h +++ b/mojo/public/cpp/bindings/lib/handle_interface_serialization.h
@@ -26,23 +26,39 @@ AssociatedInterfacePtrInfo<T>> { static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch."); + static size_t PrepareToSerialize(const AssociatedInterfacePtrInfo<T>& input, + SerializationContext* context) { + if (input.handle().is_valid()) + context->associated_endpoint_count++; + return 0; + } + static void Serialize(AssociatedInterfacePtrInfo<T>& input, AssociatedInterface_Data* output, SerializationContext* context) { DCHECK(!input.handle().is_valid() || !input.handle().is_local()); if (input.handle().is_valid()) { - DCHECK_EQ(input.handle().group_controller(), - context->group_controller.get()); + // Set to the index of the element pushed to the back of the vector. + output->handle.value = + static_cast<uint32_t>(context->associated_endpoint_handles.size()); + context->associated_endpoint_handles.push_back(input.PassHandle()); + } else { + output->handle.value = kEncodedInvalidHandleValue; } output->version = input.version(); - output->interface_id = input.PassHandle().release(); } static bool Deserialize(AssociatedInterface_Data* input, AssociatedInterfacePtrInfo<T>* output, SerializationContext* context) { - output->set_handle(context->group_controller->CreateLocalEndpointHandle( - FetchAndReset(&input->interface_id))); + if (input->handle.is_valid()) { + DCHECK_LT(input->handle.value, + context->associated_endpoint_handles.size()); + output->set_handle( + std::move(context->associated_endpoint_handles[input->handle.value])); + } else { + output->set_handle(ScopedInterfaceEndpointHandle()); + } output->set_version(input->version); return true; } @@ -53,22 +69,37 @@ AssociatedInterfaceRequest<T>> { static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch."); + static size_t PrepareToSerialize(const AssociatedInterfaceRequest<T>& input, + SerializationContext* context) { + if (input.handle().is_valid()) + context->associated_endpoint_count++; + return 0; + } + static void Serialize(AssociatedInterfaceRequest<T>& input, - AssociatedInterfaceRequest_Data* output, + AssociatedEndpointHandle_Data* output, SerializationContext* context) { DCHECK(!input.handle().is_valid() || !input.handle().is_local()); if (input.handle().is_valid()) { - DCHECK_EQ(input.handle().group_controller(), - context->group_controller.get()); + // Set to the index of the element pushed to the back of the vector. + output->value = + static_cast<uint32_t>(context->associated_endpoint_handles.size()); + context->associated_endpoint_handles.push_back(input.PassHandle()); + } else { + output->value = kEncodedInvalidHandleValue; } - output->interface_id = input.PassHandle().release(); } - static bool Deserialize(AssociatedInterfaceRequest_Data* input, + static bool Deserialize(AssociatedEndpointHandle_Data* input, AssociatedInterfaceRequest<T>* output, SerializationContext* context) { - output->Bind(context->group_controller->CreateLocalEndpointHandle( - FetchAndReset(&input->interface_id))); + if (input->is_valid()) { + DCHECK_LT(input->value, context->associated_endpoint_handles.size()); + output->Bind( + std::move(context->associated_endpoint_handles[input->value])); + } else { + output->Bind(ScopedInterfaceEndpointHandle()); + } return true; } }; @@ -77,6 +108,11 @@ struct Serializer<InterfacePtrDataView<Base>, InterfacePtr<T>> { static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch."); + static size_t PrepareToSerialize(const InterfacePtr<T>& input, + SerializationContext* context) { + return 0; + } + static void Serialize(InterfacePtr<T>& input, Interface_Data* output, SerializationContext* context) { @@ -99,6 +135,11 @@ struct Serializer<InterfaceRequestDataView<Base>, InterfaceRequest<T>> { static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch."); + static size_t PrepareToSerialize(const InterfaceRequest<T>& input, + SerializationContext* context) { + return 0; + } + static void Serialize(InterfaceRequest<T>& input, Handle_Data* output, SerializationContext* context) { @@ -115,6 +156,11 @@ template <typename T> struct Serializer<ScopedHandleBase<T>, ScopedHandleBase<T>> { + static size_t PrepareToSerialize(const ScopedHandleBase<T>& input, + SerializationContext* context) { + return 0; + } + static void Serialize(ScopedHandleBase<T>& input, Handle_Data* output, SerializationContext* context) {
diff --git a/mojo/public/cpp/bindings/lib/interface_ptr_state.h b/mojo/public/cpp/bindings/lib/interface_ptr_state.h index 76f195d..e69360561 100644 --- a/mojo/public/cpp/bindings/lib/interface_ptr_state.h +++ b/mojo/public/cpp/bindings/lib/interface_ptr_state.h
@@ -198,8 +198,6 @@ // will not be used. 0u)); proxy_.reset(new Proxy(endpoint_client_.get())); - if (Interface::PassesAssociatedKinds_) - proxy_->set_group_controller(endpoint_client_->group_controller()); } void OnQueryVersion(const base::Callback<void(uint32_t)>& callback,
diff --git a/mojo/public/cpp/bindings/lib/message.cc b/mojo/public/cpp/bindings/lib/message.cc index 0018a03b..7da4a97 100644 --- a/mojo/public/cpp/bindings/lib/message.cc +++ b/mojo/public/cpp/bindings/lib/message.cc
@@ -16,6 +16,8 @@ #include "base/logging.h" #include "base/strings/stringprintf.h" #include "base/threading/thread_local.h" +#include "mojo/public/cpp/bindings/associated_group_controller.h" +#include "mojo/public/cpp/bindings/lib/array_internal.h" namespace mojo { @@ -37,8 +39,10 @@ } Message::Message(Message&& other) - : buffer_(std::move(other.buffer_)), handles_(std::move(other.handles_)) { -} + : buffer_(std::move(other.buffer_)), + handles_(std::move(other.handles_)), + associated_endpoint_handles_( + std::move(other.associated_endpoint_handles_)) {} Message::~Message() { CloseHandles(); @@ -48,12 +52,14 @@ Reset(); std::swap(other.buffer_, buffer_); std::swap(other.handles_, handles_); + std::swap(other.associated_endpoint_handles_, associated_endpoint_handles_); return *this; } void Message::Reset() { CloseHandles(); handles_.clear(); + associated_endpoint_handles_.clear(); buffer_.reset(); } @@ -70,7 +76,52 @@ handles_.swap(*handles); } +const uint8_t* Message::payload() const { + if (version() < 2) + return data() + header()->num_bytes; + + return static_cast<const uint8_t*>(header_v2()->payload.Get()); +} + +uint32_t Message::payload_num_bytes() const { + DCHECK_GE(data_num_bytes(), header()->num_bytes); + size_t num_bytes; + if (version() < 2) { + num_bytes = data_num_bytes() - header()->num_bytes; + } else { + auto payload = reinterpret_cast<uintptr_t>(header_v2()->payload.Get()); + if (!payload) { + num_bytes = 0; + } else { + auto payload_end = + reinterpret_cast<uintptr_t>(header_v2()->payload_interface_ids.Get()); + if (!payload_end) + payload_end = reinterpret_cast<uintptr_t>(data() + data_num_bytes()); + DCHECK_GE(payload_end, payload); + num_bytes = payload_end - payload; + } + } + DCHECK_LE(num_bytes, std::numeric_limits<uint32_t>::max()); + return static_cast<uint32_t>(num_bytes); +} + +uint32_t Message::payload_num_interface_ids() const { + auto array_pointer = + version() < 2 ? nullptr : header_v2()->payload_interface_ids.Get(); + return array_pointer ? static_cast<uint32_t>(array_pointer->size()) : 0; +} + +const uint32_t* Message::payload_interface_ids() const { + auto array_pointer = + version() < 2 ? nullptr : header_v2()->payload_interface_ids.Get(); + return array_pointer ? array_pointer->storage() : nullptr; +} + ScopedMessageHandle Message::TakeMojoMessage() { + // If there are associated endpoints transferred, + // SerializeAssociatedEndpointHandles() must be called before this method. + DCHECK(associated_endpoint_handles_.empty()); + if (handles_.empty()) // Fast path for the common case: No handles. return buffer_->TakeMessage(); @@ -114,6 +165,55 @@ } } +void Message::SerializeAssociatedEndpointHandles( + AssociatedGroupController* group_controller) { + if (associated_endpoint_handles_.empty()) + return; + + DCHECK_GE(version(), 2u); + DCHECK(header_v2()->payload_interface_ids.is_null()); + + size_t size = associated_endpoint_handles_.size(); + auto data = internal::Array_Data<uint32_t>::New(size, buffer()); + header_v2()->payload_interface_ids.Set(data); + + for (size_t i = 0; i < size; ++i) { + ScopedInterfaceEndpointHandle& handle = associated_endpoint_handles_[i]; + + DCHECK(handle.is_valid()); + DCHECK(!handle.is_local()); + DCHECK_EQ(group_controller, handle.group_controller()); + data->storage()[i] = handle.release(); + } + associated_endpoint_handles_.clear(); +} + +bool Message::DeserializeAssociatedEndpointHandles( + AssociatedGroupController* group_controller) { + associated_endpoint_handles_.clear(); + + uint32_t num_ids = payload_num_interface_ids(); + if (num_ids == 0) + return true; + + associated_endpoint_handles_.reserve(num_ids); + uint32_t* ids = header_v2()->payload_interface_ids.Get()->storage(); + bool result = true; + for (uint32_t i = 0; i < num_ids; ++i) { + auto handle = group_controller->CreateLocalEndpointHandle(ids[i]); + if (IsValidInterfaceId(ids[i]) && !handle.is_valid()) { + // |ids[i]| itself is valid but handle creation failed. In that case, mark + // deserialization as failed but continue to deserialize the rest of + // handles. + result = false; + } + + associated_endpoint_handles_.push_back(std::move(handle)); + ids[i] = kInvalidInterfaceId; + } + return result; +} + PassThroughFilter::PassThroughFilter() {} PassThroughFilter::~PassThroughFilter() {} @@ -195,6 +295,8 @@ namespace internal { +MessageHeaderV2::MessageHeaderV2() = default; + MessageDispatchContext::MessageDispatchContext(Message* message) : outer_context_(current()), message_(message) { g_tls_message_dispatch_context.Get().Set(this);
diff --git a/mojo/public/cpp/bindings/lib/message_builder.cc b/mojo/public/cpp/bindings/lib/message_builder.cc index 4ffa180..6806a73 100644 --- a/mojo/public/cpp/bindings/lib/message_builder.cc +++ b/mojo/public/cpp/bindings/lib/message_builder.cc
@@ -4,11 +4,10 @@ #include "mojo/public/cpp/bindings/lib/message_builder.h" -#include <stddef.h> -#include <stdint.h> - -#include "mojo/public/cpp/bindings/lib/serialization_util.h" -#include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/bindings/lib/array_internal.h" +#include "mojo/public/cpp/bindings/lib/bindings_internal.h" +#include "mojo/public/cpp/bindings/lib/buffer.h" +#include "mojo/public/cpp/bindings/lib/message_internal.h" namespace mojo { namespace internal { @@ -19,38 +18,52 @@ (*header)->num_bytes = sizeof(Header); } -MessageBuilder::MessageBuilder(uint32_t name, size_t payload_size) { - InitializeMessage(sizeof(MessageHeader) + payload_size); +MessageBuilder::MessageBuilder(uint32_t name, + uint32_t flags, + size_t payload_size, + size_t payload_interface_id_count) { + if (payload_interface_id_count > 0) { + // Version 2 + InitializeMessage( + sizeof(MessageHeaderV2) + Align(payload_size) + + ArrayDataTraits<uint32_t>::GetStorageSize( + static_cast<uint32_t>(payload_interface_id_count))); - MessageHeader* header; - Allocate(message_.buffer(), &header); - header->version = 0; - header->name = name; + MessageHeaderV2* header; + Allocate(message_.buffer(), &header); + header->version = 2; + header->name = name; + header->flags = flags; + // The payload immediately follows the header. + header->payload.Set(header + 1); + } else if (flags & + (Message::kFlagExpectsResponse | Message::kFlagIsResponse)) { + // Version 1 + InitializeMessage(sizeof(MessageHeaderV1) + payload_size); + + MessageHeaderV1* header; + Allocate(message_.buffer(), &header); + header->version = 1; + header->name = name; + header->flags = flags; + } else { + InitializeMessage(sizeof(MessageHeader) + payload_size); + + MessageHeader* header; + Allocate(message_.buffer(), &header); + header->version = 0; + header->name = name; + header->flags = flags; + } } MessageBuilder::~MessageBuilder() { } -MessageBuilder::MessageBuilder() {} - void MessageBuilder::InitializeMessage(size_t size) { message_.Initialize(static_cast<uint32_t>(Align(size)), true /* zero_initialized */); } -MessageWithRequestIDBuilder::MessageWithRequestIDBuilder(uint32_t name, - size_t payload_size, - uint32_t flags, - uint64_t request_id) { - InitializeMessage(sizeof(MessageHeaderWithRequestID) + payload_size); - - MessageHeaderWithRequestID* header; - Allocate(message_.buffer(), &header); - header->version = 1; - header->name = name; - header->flags = flags; - header->request_id = request_id; -} - } // namespace internal } // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_builder.h b/mojo/public/cpp/bindings/lib/message_builder.h index 2e3a4a1..8a4d5c4 100644 --- a/mojo/public/cpp/bindings/lib/message_builder.h +++ b/mojo/public/cpp/bindings/lib/message_builder.h
@@ -8,25 +8,30 @@ #include <stddef.h> #include <stdint.h> +#include "base/macros.h" #include "mojo/public/cpp/bindings/bindings_export.h" -#include "mojo/public/cpp/bindings/lib/message_internal.h" #include "mojo/public/cpp/bindings/message.h" namespace mojo { + class Message; namespace internal { +class Buffer; + class MOJO_CPP_BINDINGS_EXPORT MessageBuilder { public: - MessageBuilder(uint32_t name, size_t payload_size); + MessageBuilder(uint32_t name, + uint32_t flags, + size_t payload_size, + size_t payload_interface_id_count); ~MessageBuilder(); Buffer* buffer() { return message_.buffer(); } Message* message() { return &message_; } - protected: - MessageBuilder(); + private: void InitializeMessage(size_t size); Message message_; @@ -34,52 +39,6 @@ DISALLOW_COPY_AND_ASSIGN(MessageBuilder); }; -class MOJO_CPP_BINDINGS_EXPORT MessageWithRequestIDBuilder - : public MessageBuilder { - public: - MessageWithRequestIDBuilder(uint32_t name, - size_t payload_size, - uint32_t flags, - uint64_t request_id); -}; - -class RequestMessageBuilder : public MessageWithRequestIDBuilder { - public: - RequestMessageBuilder(uint32_t name, size_t payload_size) - : MessageWithRequestIDBuilder(name, - payload_size, - Message::kFlagExpectsResponse, - 0) {} - - RequestMessageBuilder(uint32_t name, - size_t payload_size, - uint32_t extra_flags) - : MessageWithRequestIDBuilder(name, - payload_size, - Message::kFlagExpectsResponse | extra_flags, - 0) {} -}; - -class ResponseMessageBuilder : public MessageWithRequestIDBuilder { - public: - ResponseMessageBuilder(uint32_t name, - size_t payload_size, - uint64_t request_id) - : MessageWithRequestIDBuilder(name, - payload_size, - Message::kFlagIsResponse, - request_id) {} - - ResponseMessageBuilder(uint32_t name, - size_t payload_size, - uint64_t request_id, - uint32_t extra_flags) - : MessageWithRequestIDBuilder(name, - payload_size, - Message::kFlagIsResponse | extra_flags, - request_id) {} -}; - } // namespace internal } // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_header_validator.cc b/mojo/public/cpp/bindings/lib/message_header_validator.cc index ef01f0a..56be241 100644 --- a/mojo/public/cpp/bindings/lib/message_header_validator.cc +++ b/mojo/public/cpp/bindings/lib/message_header_validator.cc
@@ -4,6 +4,8 @@ #include "mojo/public/cpp/bindings/message_header_validator.h" +#include "mojo/public/cpp/bindings/lib/array_internal.h" +#include "mojo/public/cpp/bindings/lib/validate_params.h" #include "mojo/public/cpp/bindings/lib/validation_context.h" #include "mojo/public/cpp/bindings/lib/validation_errors.h" #include "mojo/public/cpp/bindings/lib/validation_util.h" @@ -11,40 +13,40 @@ namespace mojo { namespace { +// TODO(yzshen): Define a mojom struct for message header and use the generated +// validation and data view code. bool IsValidMessageHeader(const internal::MessageHeader* header, internal::ValidationContext* validation_context) { // NOTE: Our goal is to preserve support for future extension of the message // header. If we encounter fields we do not understand, we must ignore them. // Extra validation of the struct header: - if (header->version == 0) { - if (header->num_bytes != sizeof(internal::MessageHeader)) { - internal::ReportValidationError( - validation_context, - internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); - return false; + do { + if (header->version == 0) { + if (header->num_bytes == sizeof(internal::MessageHeader)) + break; + } else if (header->version == 1) { + if (header->num_bytes == sizeof(internal::MessageHeaderV1)) + break; + } else if (header->version == 2) { + if (header->num_bytes == sizeof(internal::MessageHeaderV2)) + break; + } else if (header->version > 2) { + if (header->num_bytes >= sizeof(internal::MessageHeaderV2)) + break; } - } else if (header->version == 1) { - if (header->num_bytes != sizeof(internal::MessageHeaderWithRequestID)) { - internal::ReportValidationError( - validation_context, - internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); - return false; - } - } else if (header->version > 1) { - if (header->num_bytes < sizeof(internal::MessageHeaderWithRequestID)) { - internal::ReportValidationError( - validation_context, - internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); - return false; - } - } + internal::ReportValidationError( + validation_context, + internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; + } while (false); // Validate flags (allow unknown bits): // These flags require a RequestID. - if (header->version < 1 && ((header->flags & Message::kFlagExpectsResponse) || - (header->flags & Message::kFlagIsResponse))) { + constexpr uint32_t kRequestIdFlags = + Message::kFlagExpectsResponse | Message::kFlagIsResponse; + if (header->version == 0 && (header->flags & kRequestIdFlags)) { internal::ReportValidationError( validation_context, internal::VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID); @@ -52,14 +54,50 @@ } // These flags are mutually exclusive. - if ((header->flags & Message::kFlagExpectsResponse) && - (header->flags & Message::kFlagIsResponse)) { + if ((header->flags & kRequestIdFlags) == kRequestIdFlags) { internal::ReportValidationError( validation_context, internal::VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS); return false; } + if (header->version < 2) + return true; + + auto header_v2 = static_cast<const internal::MessageHeaderV2*>(header); + // For the payload pointer: + // - Check that the pointer can be safely decoded. + // - Claim one byte that the pointer points to. It makes sure not only the + // address is within the message, but also the address precedes the array + // storing interface IDs (which is important for safely calculating the + // payload size). + // - Validation of the payload contents will be done separately based on the + // payload type. + if (!header_v2->payload.is_null() && + (!internal::ValidatePointer(header_v2->payload, validation_context) || + !validation_context->ClaimMemory(header_v2->payload.Get(), 1))) { + return false; + } + + const internal::ContainerValidateParams validate_params(0, false, nullptr); + if (!internal::ValidateContainer(header_v2->payload_interface_ids, + validation_context, &validate_params)) { + return false; + } + + if (!header_v2->payload_interface_ids.is_null()) { + size_t num_ids = header_v2->payload_interface_ids.Get()->size(); + const uint32_t* ids = header_v2->payload_interface_ids.Get()->storage(); + for (size_t i = 0; i < num_ids; ++i) { + if (!IsValidInterfaceId(ids[i]) || IsMasterInterfaceId(ids[i])) { + internal::ReportValidationError( + validation_context, + internal::VALIDATION_ERROR_ILLEGAL_INTERFACE_ID); + return false; + } + } + } + return true; } @@ -77,10 +115,10 @@ } bool MessageHeaderValidator::Accept(Message* message) { - // Pass 0 as number of handles because we don't expect any in the header, even - // if |message| contains handles. + // Pass 0 as number of handles and associated endpoint handles because we + // don't expect any in the header, even if |message| contains handles. internal::ValidationContext validation_context( - message->data(), message->data_num_bytes(), 0, message, description_); + message->data(), message->data_num_bytes(), 0, 0, message, description_); if (!internal::ValidateStructHeaderAndClaimMemory(message->data(), &validation_context))
diff --git a/mojo/public/cpp/bindings/lib/message_internal.h b/mojo/public/cpp/bindings/lib/message_internal.h index ad3ef99..6693198 100644 --- a/mojo/public/cpp/bindings/lib/message_internal.h +++ b/mojo/public/cpp/bindings/lib/message_internal.h
@@ -20,6 +20,9 @@ namespace internal { +template <typename T> +class Array_Data; + #pragma pack(push, 1) struct MessageHeader : internal::StructHeader { @@ -35,13 +38,19 @@ }; static_assert(sizeof(MessageHeader) == 24, "Bad sizeof(MessageHeader)"); -struct MessageHeaderWithRequestID : MessageHeader { +struct MessageHeaderV1 : MessageHeader { // Only used if either kFlagExpectsResponse or kFlagIsResponse is set in // order to match responses with corresponding requests. uint64_t request_id; }; -static_assert(sizeof(MessageHeaderWithRequestID) == 32, - "Bad sizeof(MessageHeaderWithRequestID)"); +static_assert(sizeof(MessageHeaderV1) == 32, "Bad sizeof(MessageHeaderV1)"); + +struct MessageHeaderV2 : MessageHeaderV1 { + MessageHeaderV2(); + GenericPointer payload; + Pointer<Array_Data<uint32_t>> payload_interface_ids; +}; +static_assert(sizeof(MessageHeaderV2) == 48, "Bad sizeof(MessageHeaderV2)"); #pragma pack(pop)
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.cc b/mojo/public/cpp/bindings/lib/multiplex_router.cc index 17f2258..d8e67f7 100644 --- a/mojo/public/cpp/bindings/lib/multiplex_router.cc +++ b/mojo/public/cpp/bindings/lib/multiplex_router.cc
@@ -37,6 +37,7 @@ id_(id), closed_(false), peer_closed_(false), + handle_created_(false), client_(nullptr), event_signalled_(false) {} @@ -61,6 +62,12 @@ peer_closed_ = true; } + bool handle_created() const { return handle_created_; } + void set_handle_created() { + router_->AssertLockAcquired(); + handle_created_ = true; + } + const base::Optional<DisconnectReason>& disconnect_reason() const { return disconnect_reason_; } @@ -134,6 +141,7 @@ bool SendMessage(Message* message) override { DCHECK(task_runner_->BelongsToCurrentThread()); + message->SerializeAssociatedEndpointHandles(router_); message->set_interface_id(id_); return router_->connector_.Accept(message); } @@ -237,6 +245,10 @@ // Whether the peer endpoint has been closed. bool peer_closed_; + // Whether there is already a ScopedInterfaceEndpointHandle created for this + // endpoint. + bool handle_created_; + base::Optional<DisconnectReason> disconnect_reason_; // The task runner on which |client_|'s methods can be called. @@ -262,12 +274,71 @@ DISALLOW_COPY_AND_ASSIGN(InterfaceEndpoint); }; +// Message objects cannot be destroyed under the router's lock, if they contain +// ScopedInterfaceEndpointHandle objects. +// IncomingMessageWrapper is used to wrap messages which haven't got the payload +// interface IDs deserialized into ScopedInterfaceEndpointHandles. Wrapper +// objects are always destroyed under the router's lock. When a wrapper is +// destroyed and the message hasn't been consumed, the wrapper is responsible +// to send endpoint closed notifications. +class MultiplexRouter::IncomingMessageWrapper { + public: + IncomingMessageWrapper() = default; + + IncomingMessageWrapper(MultiplexRouter* router, Message* message) + : router_(router), value_(std::move(*message)) { + DCHECK(value_.associated_endpoint_handles()->empty()); + } + + IncomingMessageWrapper(IncomingMessageWrapper&& other) + : router_(other.router_), value_(std::move(other.value_)) {} + + ~IncomingMessageWrapper() { + if (value_.IsNull()) + return; + + router_->AssertLockAcquired(); + + uint32_t num_ids = value_.payload_num_interface_ids(); + const uint32_t* ids = value_.payload_interface_ids(); + for (uint32_t i = 0; i < num_ids; ++i) { + MayAutoUnlock unlocker(router_->lock_.get()); + router_->control_message_proxy_.NotifyPeerEndpointClosed(ids[i], + base::nullopt); + } + } + + IncomingMessageWrapper& operator=(IncomingMessageWrapper&& other) { + router_ = other.router_; + value_ = std::move(other.value_); + return *this; + } + + // Must be called outside of the router's lock. + bool TakeMessage(Message* output) { + DCHECK(!value_.IsNull()); + + *output = std::move(value_); + return output->DeserializeAssociatedEndpointHandles(router_); + } + + const Message& value() const { return value_; } + + private: + MultiplexRouter* router_ = nullptr; + // It must not hold any ScopedInterfaceEndpointHandle objects. + Message value_; + + DISALLOW_COPY_AND_ASSIGN(IncomingMessageWrapper); +}; + struct MultiplexRouter::Task { public: // Doesn't take ownership of |message| but takes its contents. - static std::unique_ptr<Task> CreateMessageTask(Message* message) { + static std::unique_ptr<Task> CreateMessageTask( + IncomingMessageWrapper message_wrapper) { Task* task = new Task(MESSAGE); - task->message = std::move(*message); + task->message_wrapper = std::move(message_wrapper); return base::WrapUnique(task); } static std::unique_ptr<Task> CreateNotifyErrorTask( @@ -282,7 +353,7 @@ bool IsMessageTask() const { return type == MESSAGE; } bool IsNotifyErrorTask() const { return type == NOTIFY_ERROR; } - Message message; + IncomingMessageWrapper message_wrapper; scoped_refptr<InterfaceEndpoint> endpoint_to_notify; enum Type { MESSAGE, NOTIFY_ERROR }; @@ -290,6 +361,8 @@ private: explicit Task(Type in_type) : type(in_type) {} + + DISALLOW_COPY_AND_ASSIGN(Task); }; MultiplexRouter::MultiplexRouter( @@ -401,6 +474,8 @@ bool inserted = false; InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, &inserted); if (inserted) { + DCHECK(!endpoint->handle_created()); + if (encountered_error_) UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED); } else { @@ -408,7 +483,12 @@ // notification that the peer endpoint has closed. CHECK(!endpoint->closed()); CHECK(endpoint->peer_closed()); + + if (endpoint->handle_created()) + return ScopedInterfaceEndpointHandle(); } + + endpoint->set_handle_created(); return CreateScopedInterfaceEndpointHandle(id, true); } @@ -554,23 +634,25 @@ DCHECK(!paused_); + IncomingMessageWrapper message_wrapper(this, message); + ClientCallBehavior client_call_behavior = connector_.during_sync_handle_watcher_callback() ? ALLOW_DIRECT_CLIENT_CALLS_FOR_SYNC_MESSAGES : ALLOW_DIRECT_CLIENT_CALLS; - bool processed = - tasks_.empty() && ProcessIncomingMessage(message, client_call_behavior, - connector_.task_runner()); + bool processed = tasks_.empty() && ProcessIncomingMessage( + &message_wrapper, client_call_behavior, + connector_.task_runner()); if (!processed) { // Either the task queue is not empty or we cannot process the message // directly. In both cases, there is no need to call ProcessTasks(). - tasks_.push_back(Task::CreateMessageTask(message)); + tasks_.push_back(Task::CreateMessageTask(std::move(message_wrapper))); Task* task = tasks_.back().get(); - if (task->message.has_flag(Message::kFlagIsSync)) { - InterfaceId id = task->message.interface_id(); + if (task->message_wrapper.value().has_flag(Message::kFlagIsSync)) { + InterfaceId id = task->message_wrapper.value().interface_id(); sync_message_tasks_[id].push_back(task); auto iter = endpoints_.find(id); if (iter != endpoints_.end()) @@ -591,10 +673,9 @@ bool MultiplexRouter::OnPeerAssociatedEndpointClosed( InterfaceId id, const base::Optional<DisconnectReason>& reason) { - AssertLockAcquired(); - DCHECK(!IsMasterInterfaceId(id) || reason); + MayAutoLock locker(lock_.get()); InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, nullptr); if (reason) @@ -618,18 +699,18 @@ } bool MultiplexRouter::OnAssociatedEndpointClosedBeforeSent(InterfaceId id) { - AssertLockAcquired(); - if (IsMasterInterfaceId(id)) return false; - InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, nullptr); - DCHECK(!endpoint->closed()); - UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED); + { + MayAutoLock locker(lock_.get()); - MayAutoUnlock unlocker(lock_.get()); + InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, nullptr); + DCHECK(!endpoint->closed()); + UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED); + } + control_message_proxy_.NotifyPeerEndpointClosed(id, base::nullopt); - return true; } @@ -672,10 +753,11 @@ tasks_.pop_front(); InterfaceId id = kInvalidInterfaceId; - bool sync_message = task->IsMessageTask() && !task->message.IsNull() && - task->message.has_flag(Message::kFlagIsSync); + bool sync_message = + task->IsMessageTask() && !task->message_wrapper.value().IsNull() && + task->message_wrapper.value().has_flag(Message::kFlagIsSync); if (sync_message) { - id = task->message.interface_id(); + id = task->message_wrapper.value().interface_id(); auto& sync_message_queue = sync_message_tasks_[id]; DCHECK_EQ(task.get(), sync_message_queue.front()); sync_message_queue.pop_front(); @@ -685,8 +767,8 @@ task->IsNotifyErrorTask() ? ProcessNotifyErrorTask(task.get(), client_call_behavior, current_task_runner) - : ProcessIncomingMessage(&task->message, client_call_behavior, - current_task_runner); + : ProcessIncomingMessage(&task->message_wrapper, + client_call_behavior, current_task_runner); if (!processed) { if (sync_message) { @@ -719,11 +801,11 @@ iter->second.pop_front(); DCHECK(task->IsMessageTask()); - Message message = std::move(task->message); + IncomingMessageWrapper message_wrapper = std::move(task->message_wrapper); // Note: after this call, |task| and |iter| may be invalidated. bool processed = ProcessIncomingMessage( - &message, ALLOW_DIRECT_CLIENT_CALLS_FOR_SYNC_MESSAGES, nullptr); + &message_wrapper, ALLOW_DIRECT_CLIENT_CALLS_FOR_SYNC_MESSAGES, nullptr); DCHECK(processed); iter = sync_message_tasks_.find(id); @@ -775,27 +857,38 @@ } bool MultiplexRouter::ProcessIncomingMessage( - Message* message, + IncomingMessageWrapper* message_wrapper, ClientCallBehavior client_call_behavior, base::SingleThreadTaskRunner* current_task_runner) { DCHECK(!current_task_runner || current_task_runner->BelongsToCurrentThread()); DCHECK(!paused_); - DCHECK(message); + DCHECK(message_wrapper); AssertLockAcquired(); - if (message->IsNull()) { + if (message_wrapper->value().IsNull()) { // This is a sync message and has been processed during sync handle // watching. return true; } - if (PipeControlMessageHandler::IsPipeControlMessage(message)) { - if (!control_message_handler_.Accept(message)) + if (PipeControlMessageHandler::IsPipeControlMessage( + &message_wrapper->value())) { + bool result = false; + + { + MayAutoUnlock unlocker(lock_.get()); + Message message; + result = message_wrapper->TakeMessage(&message) && + control_message_handler_.Accept(&message); + } + + if (!result) RaiseErrorInNonTestingMode(); + return true; } - InterfaceId id = message->interface_id(); + InterfaceId id = message_wrapper->value().interface_id(); DCHECK(IsValidInterfaceId(id)); bool inserted = false; @@ -832,7 +925,7 @@ } bool can_direct_call; - if (message->has_flag(Message::kFlagIsSync)) { + if (message_wrapper->value().has_flag(Message::kFlagIsSync)) { can_direct_call = client_call_behavior != NO_DIRECT_CLIENT_CALLS && endpoint->task_runner()->BelongsToCurrentThread(); } else { @@ -857,7 +950,9 @@ // It is safe to call into |client| without the lock. Because |client| is // always accessed on the same thread, including DetachEndpointClient(). MayAutoUnlock unlocker(lock_.get()); - result = client->HandleIncomingMessage(message); + Message message; + result = message_wrapper->TakeMessage(&message) && + client->HandleIncomingMessage(&message); } if (!result) RaiseErrorInNonTestingMode();
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.h b/mojo/public/cpp/bindings/lib/multiplex_router.h index c1e0298..ac10f93 100644 --- a/mojo/public/cpp/bindings/lib/multiplex_router.h +++ b/mojo/public/cpp/bindings/lib/multiplex_router.h
@@ -154,6 +154,7 @@ private: class InterfaceEndpoint; + class IncomingMessageWrapper; struct Task; ~MultiplexRouter() override; @@ -208,7 +209,7 @@ ClientCallBehavior client_call_behavior, base::SingleThreadTaskRunner* current_task_runner); bool ProcessIncomingMessage( - Message* message, + IncomingMessageWrapper* message_wrapper, ClientCallBehavior client_call_behavior, base::SingleThreadTaskRunner* current_task_runner);
diff --git a/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc b/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc index db04c6d..bf3bb97d 100644 --- a/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc +++ b/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc
@@ -5,8 +5,10 @@ #include "mojo/public/cpp/bindings/pipe_control_message_handler.h" #include "base/logging.h" +#include "mojo/public/cpp/bindings/interface_id.h" #include "mojo/public/cpp/bindings/lib/message_builder.h" #include "mojo/public/cpp/bindings/lib/serialization.h" +#include "mojo/public/cpp/bindings/lib/serialization_context.h" #include "mojo/public/cpp/bindings/lib/validation_context.h" #include "mojo/public/cpp/bindings/lib/validation_util.h" #include "mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h" @@ -41,8 +43,9 @@ } bool PipeControlMessageHandler::Validate(Message* message) { - internal::ValidationContext validation_context( - message->data(), message->data_num_bytes(), 0, message, description_); + internal::ValidationContext validation_context(message->payload(), + message->payload_num_bytes(), + 0, 0, message, description_); if (message->name() == pipe_control::kRunOrClosePipeMessageId) { if (!internal::ValidateMessageIsRequestWithoutResponse( @@ -58,13 +61,14 @@ } bool PipeControlMessageHandler::RunOrClosePipe(Message* message) { + internal::SerializationContext context; pipe_control::internal::RunOrClosePipeMessageParams_Data* params = reinterpret_cast< pipe_control::internal::RunOrClosePipeMessageParams_Data*>( message->mutable_payload()); pipe_control::RunOrClosePipeMessageParamsPtr params_ptr; internal::Deserialize<pipe_control::RunOrClosePipeMessageParamsDataView>( - params, ¶ms_ptr, &context_); + params, ¶ms_ptr, &context); if (params_ptr->input->is_peer_associated_endpoint_closed_event()) { const auto& event =
diff --git a/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc b/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc index 87b59c35c..30daac7 100644 --- a/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc +++ b/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc
@@ -25,8 +25,8 @@ size_t size = internal::PrepareToSerialize< pipe_control::RunOrClosePipeMessageParamsDataView>(params_ptr, &context); - internal::MessageBuilder builder(pipe_control::kRunOrClosePipeMessageId, - size); + internal::MessageBuilder builder(pipe_control::kRunOrClosePipeMessageId, 0, + size, 0); pipe_control::internal::RunOrClosePipeMessageParams_Data* params = nullptr; internal::Serialize<pipe_control::RunOrClosePipeMessageParamsDataView>(
diff --git a/mojo/public/cpp/bindings/lib/serialization.h b/mojo/public/cpp/bindings/lib/serialization.h index 6f0b6af..359b02b77 100644 --- a/mojo/public/cpp/bindings/lib/serialization.h +++ b/mojo/public/cpp/bindings/lib/serialization.h
@@ -84,7 +84,7 @@ memcpy(input_buffer, &input.front(), input.size()); } - ValidationContext validation_context(input_buffer, input.size(), 0); + ValidationContext validation_context(input_buffer, input.size(), 0, 0); bool result = false; if (DataType::Validate(input_buffer, &validation_context)) { auto data = reinterpret_cast<DataType*>(input_buffer);
diff --git a/mojo/public/cpp/bindings/lib/serialization_context.cc b/mojo/public/cpp/bindings/lib/serialization_context.cc index 7fd80be..e2fd5c6 100644 --- a/mojo/public/cpp/bindings/lib/serialization_context.cc +++ b/mojo/public/cpp/bindings/lib/serialization_context.cc
@@ -7,7 +7,6 @@ #include <limits> #include "base/logging.h" -#include "mojo/public/cpp/bindings/associated_group_controller.h" #include "mojo/public/cpp/system/core.h" namespace mojo { @@ -50,10 +49,6 @@ SerializationContext::SerializationContext() {} -SerializationContext::SerializationContext( - scoped_refptr<AssociatedGroupController> in_group_controller) - : group_controller(std::move(in_group_controller)) {} - SerializationContext::~SerializationContext() { DCHECK(!custom_contexts || custom_contexts->empty()); }
diff --git a/mojo/public/cpp/bindings/lib/serialization_context.h b/mojo/public/cpp/bindings/lib/serialization_context.h index 4bded30..a34fe3d 100644 --- a/mojo/public/cpp/bindings/lib/serialization_context.h +++ b/mojo/public/cpp/bindings/lib/serialization_context.h
@@ -12,15 +12,12 @@ #include <vector> #include "base/macros.h" -#include "base/memory/ref_counted.h" #include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/lib/bindings_internal.h" +#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" #include "mojo/public/cpp/system/handle.h" namespace mojo { - -class AssociatedGroupController; - namespace internal { // A container for handles during serialization/deserialization. @@ -57,19 +54,21 @@ // Context information for serialization/deserialization routines. struct MOJO_CPP_BINDINGS_EXPORT SerializationContext { SerializationContext(); - explicit SerializationContext( - scoped_refptr<AssociatedGroupController> in_group_controller); ~SerializationContext(); - // Used to serialize/deserialize associated interface pointers and requests. - scoped_refptr<AssociatedGroupController> group_controller; - // Opaque context pointers returned by StringTraits::SetUpContext(). std::unique_ptr<std::queue<void*>> custom_contexts; // Stashes handles encoded in a message by index. SerializedHandleVector handles; + + // The number of ScopedInterfaceEndpointHandles that need to be serialized. + // It is calculated by PrepareToSerialize(). + uint32_t associated_endpoint_count = 0; + + // Stashes ScopedInterfaceEndpointHandles encoded in a message by index. + std::vector<ScopedInterfaceEndpointHandle> associated_endpoint_handles; }; } // namespace internal
diff --git a/mojo/public/cpp/bindings/lib/validation_context.cc b/mojo/public/cpp/bindings/lib/validation_context.cc index 43516865..ad0a364 100644 --- a/mojo/public/cpp/bindings/lib/validation_context.cc +++ b/mojo/public/cpp/bindings/lib/validation_context.cc
@@ -12,6 +12,7 @@ ValidationContext::ValidationContext(const void* data, size_t data_num_bytes, size_t num_handles, + size_t num_associated_endpoint_handles, Message* message, const base::StringPiece& description, int stack_depth) @@ -21,20 +22,25 @@ data_end_(data_begin_ + data_num_bytes), handle_begin_(0), handle_end_(static_cast<uint32_t>(num_handles)), + associated_endpoint_handle_begin_(0), + associated_endpoint_handle_end_( + static_cast<uint32_t>(num_associated_endpoint_handles)), stack_depth_(stack_depth) { + // Check whether the calculation of |data_end_| or static_cast from size_t to + // uint32_t causes overflow. + // They shouldn't happen but they do, set the corresponding range to empty. if (data_end_ < data_begin_) { - // The calculation of |data_end_| overflowed. - // It shouldn't happen but if it does, set the range to empty so - // IsValidRange() and ClaimMemory() always fail. NOTREACHED(); data_end_ = data_begin_; } if (handle_end_ < num_handles) { - // Assigning |num_handles| to |handle_end_| overflowed. - // It shouldn't happen but if it does, set the handle index range to empty. NOTREACHED(); handle_end_ = 0; } + if (associated_endpoint_handle_end_ < num_associated_endpoint_handles) { + NOTREACHED(); + associated_endpoint_handle_end_ = 0; + } } ValidationContext::~ValidationContext() {
diff --git a/mojo/public/cpp/bindings/lib/validation_context.h b/mojo/public/cpp/bindings/lib/validation_context.h index f8fe58ea..ed6c6542 100644 --- a/mojo/public/cpp/bindings/lib/validation_context.h +++ b/mojo/public/cpp/bindings/lib/validation_context.h
@@ -28,6 +28,8 @@ public: // [data, data + data_num_bytes) specifies the initial valid memory range. // [0, num_handles) specifies the initial valid range of handle indices. + // [0, num_associated_endpoint_handles) specifies the initial valid range of + // associated endpoint handle indices. // // If provided, |message| and |description| provide additional information // to use when reporting validation errors. In addition if |message| is @@ -36,6 +38,7 @@ ValidationContext(const void* data, size_t data_num_bytes, size_t num_handles, + size_t num_associated_endpoint_handles, Message* message = nullptr, const base::StringPiece& description = "", int stack_depth = 0); @@ -77,6 +80,28 @@ return true; } + // Claims the specified encoded associated endpoint handle. + // The method succeeds if: + // - |encoded_handle|'s value is |kEncodedInvalidHandleValue|. + // - the handle is contained inside the valid range of associated endpoint + // handle indices. In this case, the valid range is shinked to begin right + // after the claimed handle. + bool ClaimAssociatedEndpointHandle( + const AssociatedEndpointHandle_Data& encoded_handle) { + uint32_t index = encoded_handle.value; + if (index == kEncodedInvalidHandleValue) + return true; + + if (index < associated_endpoint_handle_begin_ || + index >= associated_endpoint_handle_end_) + return false; + + // |index| + 1 shouldn't overflow, because |index| is not the max value of + // uint32_t (it is less than |associated_endpoint_handle_end_|). + associated_endpoint_handle_begin_ = index + 1; + return true; + } + // Returns true if the specified range is not empty, and the range is // contained inside the valid memory range. bool IsValidRange(const void* position, uint32_t num_bytes) const { @@ -128,6 +153,11 @@ uint32_t handle_begin_; uint32_t handle_end_; + // [associated_endpoint_handle_begin_, associated_endpoint_handle_end_) is the + // valid associated endpoint handle index range. + uint32_t associated_endpoint_handle_begin_; + uint32_t associated_endpoint_handle_end_; + int stack_depth_; DISALLOW_COPY_AND_ASSIGN(ValidationContext);
diff --git a/mojo/public/cpp/bindings/lib/validation_errors.cc b/mojo/public/cpp/bindings/lib/validation_errors.cc index 12e6ff7..904f5e4c 100644 --- a/mojo/public/cpp/bindings/lib/validation_errors.cc +++ b/mojo/public/cpp/bindings/lib/validation_errors.cc
@@ -98,10 +98,7 @@ mojo::Message* message, ValidationError error, const char* description) { - ValidationContext validation_context( - message->data(), message->data_num_bytes(), - message->handles()->size(), message, - description); + ValidationContext validation_context(nullptr, 0, 0, 0, message, description); ReportValidationError(&validation_context, error); }
diff --git a/mojo/public/cpp/bindings/lib/validation_util.cc b/mojo/public/cpp/bindings/lib/validation_util.cc index 9675afe..7614df5 100644 --- a/mojo/public/cpp/bindings/lib/validation_util.cc +++ b/mojo/public/cpp/bindings/lib/validation_util.cc
@@ -101,11 +101,11 @@ } bool IsHandleOrInterfaceValid(const AssociatedInterface_Data& input) { - return IsValidInterfaceId(input.interface_id); + return input.handle.is_valid(); } -bool IsHandleOrInterfaceValid(const AssociatedInterfaceRequest_Data& input) { - return IsValidInterfaceId(input.interface_id); +bool IsHandleOrInterfaceValid(const AssociatedEndpointHandle_Data& input) { + return input.is_valid(); } bool IsHandleOrInterfaceValid(const Interface_Data& input) { @@ -130,7 +130,7 @@ } bool ValidateHandleOrInterfaceNonNullable( - const AssociatedInterfaceRequest_Data& input, + const AssociatedEndpointHandle_Data& input, const char* error_message, ValidationContext* validation_context) { if (IsHandleOrInterfaceValid(input)) @@ -170,7 +170,7 @@ bool ValidateHandleOrInterface(const AssociatedInterface_Data& input, ValidationContext* validation_context) { - if (!IsMasterInterfaceId(input.interface_id)) + if (validation_context->ClaimAssociatedEndpointHandle(input.handle)) return true; ReportValidationError(validation_context, @@ -178,9 +178,9 @@ return false; } -bool ValidateHandleOrInterface(const AssociatedInterfaceRequest_Data& input, +bool ValidateHandleOrInterface(const AssociatedEndpointHandle_Data& input, ValidationContext* validation_context) { - if (!IsMasterInterfaceId(input.interface_id)) + if (validation_context->ClaimAssociatedEndpointHandle(input)) return true; ReportValidationError(validation_context,
diff --git a/mojo/public/cpp/bindings/lib/validation_util.h b/mojo/public/cpp/bindings/lib/validation_util.h index 8de5569..ea5a9916 100644 --- a/mojo/public/cpp/bindings/lib/validation_util.h +++ b/mojo/public/cpp/bindings/lib/validation_util.h
@@ -112,7 +112,7 @@ MOJO_CPP_BINDINGS_EXPORT bool IsHandleOrInterfaceValid( const AssociatedInterface_Data& input); MOJO_CPP_BINDINGS_EXPORT bool IsHandleOrInterfaceValid( - const AssociatedInterfaceRequest_Data& input); + const AssociatedEndpointHandle_Data& input); MOJO_CPP_BINDINGS_EXPORT bool IsHandleOrInterfaceValid( const Interface_Data& input); MOJO_CPP_BINDINGS_EXPORT bool IsHandleOrInterfaceValid( @@ -123,7 +123,7 @@ const char* error_message, ValidationContext* validation_context); MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable( - const AssociatedInterfaceRequest_Data& input, + const AssociatedEndpointHandle_Data& input, const char* error_message, ValidationContext* validation_context); MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable( @@ -191,7 +191,7 @@ const AssociatedInterface_Data& input, ValidationContext* validation_context); MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterface( - const AssociatedInterfaceRequest_Data& input, + const AssociatedEndpointHandle_Data& input, ValidationContext* validation_context); MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterface( const Interface_Data& input,
diff --git a/mojo/public/cpp/bindings/message.h b/mojo/public/cpp/bindings/message.h index cfb9d05..65d6cec 100644 --- a/mojo/public/cpp/bindings/message.h +++ b/mojo/public/cpp/bindings/message.h
@@ -19,10 +19,13 @@ #include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/lib/message_buffer.h" #include "mojo/public/cpp/bindings/lib/message_internal.h" +#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" #include "mojo/public/cpp/system/message.h" namespace mojo { +class AssociatedGroupController; + using ReportBadMessageCallback = base::Callback<void(const std::string& error)>; // Message is a holder for the data and handles to be sent over a MessagePipe. @@ -73,12 +76,30 @@ const internal::MessageHeader* header() const { return static_cast<const internal::MessageHeader*>(buffer_->data()); } - internal::MessageHeader* header() { - return const_cast<internal::MessageHeader*>( - static_cast<const Message*>(this)->header()); + return static_cast<internal::MessageHeader*>(buffer_->data()); } + const internal::MessageHeaderV1* header_v1() const { + DCHECK_GE(version(), 1u); + return static_cast<const internal::MessageHeaderV1*>(buffer_->data()); + } + internal::MessageHeaderV1* header_v1() { + DCHECK_GE(version(), 1u); + return static_cast<internal::MessageHeaderV1*>(buffer_->data()); + } + + const internal::MessageHeaderV2* header_v2() const { + DCHECK_GE(version(), 2u); + return static_cast<const internal::MessageHeaderV2*>(buffer_->data()); + } + internal::MessageHeaderV2* header_v2() { + DCHECK_GE(version(), 2u); + return static_cast<internal::MessageHeaderV2*>(buffer_->data()); + } + + uint32_t version() const { return header()->version; } + uint32_t interface_id() const { return header()->interface_id; } void set_interface_id(uint32_t id) { header()->interface_id = id; } @@ -86,32 +107,32 @@ bool has_flag(uint32_t flag) const { return !!(header()->flags & flag); } // Access the request_id field (if present). - bool has_request_id() const { return header()->version >= 1; } - uint64_t request_id() const { - DCHECK(has_request_id()); - return static_cast<const internal::MessageHeaderWithRequestID*>( - header())->request_id; - } + uint64_t request_id() const { return header_v1()->request_id; } void set_request_id(uint64_t request_id) { - DCHECK(has_request_id()); - static_cast<internal::MessageHeaderWithRequestID*>(header()) - ->request_id = request_id; + header_v1()->request_id = request_id; } // Access the payload. - const uint8_t* payload() const { return data() + header()->num_bytes; } + const uint8_t* payload() const; uint8_t* mutable_payload() { return const_cast<uint8_t*>(payload()); } - uint32_t payload_num_bytes() const { - DCHECK(data_num_bytes() >= header()->num_bytes); - size_t num_bytes = data_num_bytes() - header()->num_bytes; - DCHECK(num_bytes <= std::numeric_limits<uint32_t>::max()); - return static_cast<uint32_t>(num_bytes); - } + uint32_t payload_num_bytes() const; + + uint32_t payload_num_interface_ids() const; + const uint32_t* payload_interface_ids() const; // Access the handles. const std::vector<Handle>* handles() const { return &handles_; } std::vector<Handle>* mutable_handles() { return &handles_; } + const std::vector<ScopedInterfaceEndpointHandle>* + associated_endpoint_handles() const { + return &associated_endpoint_handles_; + } + std::vector<ScopedInterfaceEndpointHandle>* + mutable_associated_endpoint_handles() { + return &associated_endpoint_handles_; + } + // Access the underlying Buffer interface. internal::Buffer* buffer() { return buffer_.get(); } @@ -124,11 +145,22 @@ // rejected by bindings validation code. void NotifyBadMessage(const std::string& error); + // Serializes |associated_endpoint_handles_| into the payload_interface_ids + // field. + void SerializeAssociatedEndpointHandles( + AssociatedGroupController* group_controller); + + // Deserializes |associated_endpoint_handles_| from the payload_interface_ids + // field. + bool DeserializeAssociatedEndpointHandles( + AssociatedGroupController* group_controller); + private: void CloseHandles(); std::unique_ptr<internal::MessageBuffer> buffer_; std::vector<Handle> handles_; + std::vector<ScopedInterfaceEndpointHandle> associated_endpoint_handles_; DISALLOW_COPY_AND_ASSIGN(Message); };
diff --git a/mojo/public/cpp/bindings/pipe_control_message_handler.h b/mojo/public/cpp/bindings/pipe_control_message_handler.h index b33ffd3..a5c04da 100644 --- a/mojo/public/cpp/bindings/pipe_control_message_handler.h +++ b/mojo/public/cpp/bindings/pipe_control_message_handler.h
@@ -10,8 +10,6 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "mojo/public/cpp/bindings/bindings_export.h" -#include "mojo/public/cpp/bindings/interface_id.h" -#include "mojo/public/cpp/bindings/lib/serialization_context.h" #include "mojo/public/cpp/bindings/message.h" namespace mojo { @@ -48,7 +46,6 @@ std::string description_; PipeControlMessageHandlerDelegate* const delegate_; - internal::SerializationContext context_; DISALLOW_COPY_AND_ASSIGN(PipeControlMessageHandler); };
diff --git a/mojo/public/cpp/bindings/tests/bindings_perftest.cc b/mojo/public/cpp/bindings/tests/bindings_perftest.cc index 4225815..6a50de4 100644 --- a/mojo/public/cpp/bindings/tests/bindings_perftest.cc +++ b/mojo/public/cpp/bindings/tests/bindings_perftest.cc
@@ -154,7 +154,7 @@ } } - internal::MessageBuilder builder(count, 8); + internal::MessageBuilder builder(count, 0, 8, 0); bool result = sender_->Accept(builder.message()); DCHECK(result); return true; @@ -173,7 +173,7 @@ quit_closure_ = run_loop.QuitClosure(); start_time_ = base::TimeTicks::Now(); - internal::MessageBuilder builder(0, 8); + internal::MessageBuilder builder(0, 0, 8, 0); bool result = sender_->Accept(builder.message()); DCHECK(result); @@ -262,7 +262,7 @@ receiver.Reset(); base::TimeTicks start_time = base::TimeTicks::Now(); for (size_t j = 0; j < kIterations[i]; ++j) { - internal::MessageBuilder builder(0, 8); + internal::MessageBuilder builder(0, 0, 8, 0); bool result = router->SimulateReceivingMessageForTesting(builder.message()); DCHECK(result);
diff --git a/mojo/public/cpp/bindings/tests/connector_unittest.cc b/mojo/public/cpp/bindings/tests/connector_unittest.cc index 87f88924..74ecb7a 100644 --- a/mojo/public/cpp/bindings/tests/connector_unittest.cc +++ b/mojo/public/cpp/bindings/tests/connector_unittest.cc
@@ -99,7 +99,7 @@ void AllocMessage(const char* text, Message* message) { size_t payload_size = strlen(text) + 1; // Plus null terminator. - internal::MessageBuilder builder(1, payload_size); + internal::MessageBuilder builder(1, 0, payload_size, 0); memcpy(builder.buffer()->Allocate(payload_size), text, payload_size); *message = std::move(*builder.message());
diff --git a/mojo/public/cpp/bindings/tests/router_test_util.cc b/mojo/public/cpp/bindings/tests/router_test_util.cc index 3119912..b9b93d8 100644 --- a/mojo/public/cpp/bindings/tests/router_test_util.cc +++ b/mojo/public/cpp/bindings/tests/router_test_util.cc
@@ -17,7 +17,8 @@ void AllocRequestMessage(uint32_t name, const char* text, Message* message) { size_t payload_size = strlen(text) + 1; // Plus null terminator. - internal::RequestMessageBuilder builder(name, payload_size); + internal::MessageBuilder builder(name, Message::kFlagExpectsResponse, + payload_size, 0); memcpy(builder.buffer()->Allocate(payload_size), text, payload_size); *message = std::move(*builder.message()); } @@ -27,7 +28,9 @@ uint64_t request_id, Message* message) { size_t payload_size = strlen(text) + 1; // Plus null terminator. - internal::ResponseMessageBuilder builder(name, payload_size, request_id); + internal::MessageBuilder builder(name, Message::kFlagIsResponse, payload_size, + 0); + builder.message()->set_request_id(request_id); memcpy(builder.buffer()->Allocate(payload_size), text, payload_size); *message = std::move(*builder.message()); }
diff --git a/mojo/public/cpp/bindings/tests/union_unittest.cc b/mojo/public/cpp/bindings/tests/union_unittest.cc index 6b4e0986..bdf27dff 100644 --- a/mojo/public/cpp/bindings/tests/union_unittest.cc +++ b/mojo/public/cpp/bindings/tests/union_unittest.cc
@@ -173,7 +173,7 @@ void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); EXPECT_TRUE( internal::PodUnion_Data::Validate(raw_buf, &validation_context, false)); free(raw_buf); @@ -224,7 +224,7 @@ TEST(UnionTest, NullValidation) { void* buf = nullptr; - mojo::internal::ValidationContext validation_context(buf, 0, 0); + mojo::internal::ValidationContext validation_context(buf, 0, 0, 0); EXPECT_TRUE(internal::PodUnion_Data::Validate( buf, &validation_context, false)); } @@ -239,7 +239,7 @@ internal::PodUnion_Data* data = reinterpret_cast<internal::PodUnion_Data*>(buf); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); EXPECT_FALSE(internal::PodUnion_Data::Validate( buf, &validation_context, false)); free(raw_buf); @@ -250,7 +250,7 @@ mojo::internal::FixedBufferForTesting buf(size); internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); void* raw_buf = buf.Leak(); EXPECT_FALSE( internal::PodUnion_Data::Validate(raw_buf, &validation_context, false)); @@ -263,7 +263,7 @@ internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf); data->tag = static_cast<internal::PodUnion_Data::PodUnion_Tag>(0xFFFFFF); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); void* raw_buf = buf.Leak(); EXPECT_FALSE( internal::PodUnion_Data::Validate(raw_buf, &validation_context, false)); @@ -284,7 +284,7 @@ void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); EXPECT_FALSE( internal::PodUnion_Data::Validate(raw_buf, &validation_context, false)); free(raw_buf); @@ -304,7 +304,7 @@ void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); EXPECT_TRUE( internal::PodUnion_Data::Validate(raw_buf, &validation_context, false)); free(raw_buf); @@ -370,7 +370,7 @@ data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING; data->data.unknown = 0x0; mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); void* raw_buf = buf.Leak(); EXPECT_FALSE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); @@ -384,7 +384,7 @@ data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING; data->data.unknown = 0xFFFFFFFFFFFFFFFF; mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); void* raw_buf = buf.Leak(); EXPECT_FALSE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); @@ -403,7 +403,7 @@ reinterpret_cast<mojo::internal::ArrayHeader*>(ptr + *ptr); array_header->num_bytes = 20; // This should go out of bounds. array_header->num_elements = 20; - mojo::internal::ValidationContext validation_context(data, 32, 0); + mojo::internal::ValidationContext validation_context(data, 32, 0, 0); void* raw_buf = buf.Leak(); EXPECT_FALSE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); @@ -515,7 +515,7 @@ reinterpret_cast<mojo::internal::Array_Data<internal::ObjectUnion_Data>*>( new_buf.data()); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); ASSERT_TRUE(mojo::internal::Array_Data<internal::ObjectUnion_Data>::Validate( data, &validation_context, &validate_params)); @@ -601,7 +601,7 @@ void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); EXPECT_TRUE(internal::SmallStruct_Data::Validate( raw_buf, &validation_context)); free(raw_buf); @@ -625,7 +625,7 @@ void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); EXPECT_FALSE(internal::SmallStruct_Data::Validate( raw_buf, &validation_context)); free(raw_buf); @@ -646,7 +646,7 @@ void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); EXPECT_FALSE(internal::SmallStructNonNullableUnion_Data::Validate( raw_buf, &validation_context)); free(raw_buf); @@ -667,7 +667,7 @@ void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); EXPECT_TRUE(internal::SmallStruct_Data::Validate( raw_buf, &validation_context)); free(raw_buf); @@ -794,7 +794,7 @@ void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); EXPECT_TRUE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); @@ -818,7 +818,7 @@ void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); EXPECT_FALSE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); @@ -840,7 +840,7 @@ void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); EXPECT_TRUE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); @@ -899,7 +899,7 @@ void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); EXPECT_TRUE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); @@ -963,7 +963,7 @@ void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); EXPECT_TRUE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); @@ -1019,7 +1019,7 @@ void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); EXPECT_TRUE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); @@ -1043,7 +1043,7 @@ void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 0); + data, static_cast<uint32_t>(size), 0, 0); EXPECT_FALSE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); @@ -1120,7 +1120,7 @@ void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 1); + data, static_cast<uint32_t>(size), 1, 0); EXPECT_TRUE(internal::HandleUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); @@ -1145,7 +1145,7 @@ void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( - data, static_cast<uint32_t>(size), 1); + data, static_cast<uint32_t>(size), 1, 0); EXPECT_FALSE(internal::HandleUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf);
diff --git a/mojo/public/cpp/bindings/tests/validation_context_unittest.cc b/mojo/public/cpp/bindings/tests/validation_context_unittest.cc index aceaf1c0..9ce9d60e 100644 --- a/mojo/public/cpp/bindings/tests/validation_context_unittest.cc +++ b/mojo/public/cpp/bindings/tests/validation_context_unittest.cc
@@ -17,6 +17,8 @@ namespace { using Handle_Data = mojo::internal::Handle_Data; +using AssociatedEndpointHandle_Data = + mojo::internal::AssociatedEndpointHandle_Data; const void* ToPtr(uintptr_t ptr) { return reinterpret_cast<const void*>(ptr); @@ -27,7 +29,7 @@ { // Test memory range overflow. internal::ValidationContext context( - ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 5000, 0); + ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 5000, 0, 0); EXPECT_FALSE(context.IsValidRange( ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 1)); @@ -35,11 +37,14 @@ ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 1)); } - if (sizeof(size_t) > sizeof(uint32_t)) { + if (sizeof(size_t) <= sizeof(uint32_t)) + return; + + { // Test handle index range overflow. size_t num_handles = static_cast<size_t>(std::numeric_limits<uint32_t>::max()) + 5; - internal::ValidationContext context(ToPtr(0), 0, num_handles); + internal::ValidationContext context(ToPtr(0), 0, num_handles, 0); EXPECT_FALSE(context.ClaimHandle(Handle_Data(0))); EXPECT_FALSE(context.ClaimHandle( @@ -48,12 +53,28 @@ EXPECT_TRUE(context.ClaimHandle( Handle_Data(internal::kEncodedInvalidHandleValue))); } + + { + size_t num_associated_endpoint_handles = + static_cast<size_t>(std::numeric_limits<uint32_t>::max()) + 5; + internal::ValidationContext context(ToPtr(0), 0, 0, + num_associated_endpoint_handles); + + EXPECT_FALSE(context.ClaimAssociatedEndpointHandle( + AssociatedEndpointHandle_Data(0))); + EXPECT_FALSE( + context.ClaimAssociatedEndpointHandle(AssociatedEndpointHandle_Data( + std::numeric_limits<uint32_t>::max() - 1))); + + EXPECT_TRUE(context.ClaimAssociatedEndpointHandle( + AssociatedEndpointHandle_Data(internal::kEncodedInvalidHandleValue))); + } } #endif TEST(ValidationContextTest, IsValidRange) { { - internal::ValidationContext context(ToPtr(1234), 100, 0); + internal::ValidationContext context(ToPtr(1234), 100, 0, 0); // Basics. EXPECT_FALSE(context.IsValidRange(ToPtr(100), 5)); @@ -79,7 +100,7 @@ } { - internal::ValidationContext context(ToPtr(1234), 100, 0); + internal::ValidationContext context(ToPtr(1234), 100, 0, 0); // Should return false for empty ranges. EXPECT_FALSE(context.IsValidRange(ToPtr(0), 0)); EXPECT_FALSE(context.IsValidRange(ToPtr(1200), 0)); @@ -90,7 +111,7 @@ { // The valid memory range is empty. - internal::ValidationContext context(ToPtr(1234), 0, 0); + internal::ValidationContext context(ToPtr(1234), 0, 0, 0); EXPECT_FALSE(context.IsValidRange(ToPtr(1234), 1)); EXPECT_FALSE(context.IsValidRange(ToPtr(1234), 0)); @@ -98,7 +119,7 @@ { internal::ValidationContext context( - ToPtr(std::numeric_limits<uintptr_t>::max() - 2000), 1000, 0); + ToPtr(std::numeric_limits<uintptr_t>::max() - 2000), 1000, 0, 0); // Test overflow. EXPECT_FALSE(context.IsValidRange( @@ -115,7 +136,7 @@ TEST(ValidationContextTest, ClaimHandle) { { - internal::ValidationContext context(ToPtr(0), 0, 10); + internal::ValidationContext context(ToPtr(0), 0, 10, 0); // Basics. EXPECT_TRUE(context.ClaimHandle(Handle_Data(0))); @@ -137,7 +158,7 @@ { // No handle to claim. - internal::ValidationContext context(ToPtr(0), 0, 0); + internal::ValidationContext context(ToPtr(0), 0, 0, 0); EXPECT_FALSE(context.ClaimHandle(Handle_Data(0))); @@ -152,7 +173,7 @@ EXPECT_EQ(internal::kEncodedInvalidHandleValue, std::numeric_limits<uint32_t>::max()); internal::ValidationContext context( - ToPtr(0), 0, std::numeric_limits<uint32_t>::max()); + ToPtr(0), 0, std::numeric_limits<uint32_t>::max(), 0); EXPECT_TRUE(context.ClaimHandle( Handle_Data(std::numeric_limits<uint32_t>::max() - 1))); @@ -166,9 +187,71 @@ } } +TEST(ValidationContextTest, ClaimAssociatedEndpointHandle) { + { + internal::ValidationContext context(ToPtr(0), 0, 0, 10); + + // Basics. + EXPECT_TRUE(context.ClaimAssociatedEndpointHandle( + AssociatedEndpointHandle_Data(0))); + EXPECT_FALSE(context.ClaimAssociatedEndpointHandle( + AssociatedEndpointHandle_Data(0))); + + EXPECT_TRUE(context.ClaimAssociatedEndpointHandle( + AssociatedEndpointHandle_Data(9))); + EXPECT_FALSE(context.ClaimAssociatedEndpointHandle( + AssociatedEndpointHandle_Data(10))); + + // Should fail because it is smaller than the max index that has been + // claimed. + EXPECT_FALSE(context.ClaimAssociatedEndpointHandle( + AssociatedEndpointHandle_Data(8))); + + // Should return true for invalid handle. + EXPECT_TRUE(context.ClaimAssociatedEndpointHandle( + AssociatedEndpointHandle_Data(internal::kEncodedInvalidHandleValue))); + EXPECT_TRUE(context.ClaimAssociatedEndpointHandle( + AssociatedEndpointHandle_Data(internal::kEncodedInvalidHandleValue))); + } + + { + // No handle to claim. + internal::ValidationContext context(ToPtr(0), 0, 0, 0); + + EXPECT_FALSE(context.ClaimAssociatedEndpointHandle( + AssociatedEndpointHandle_Data(0))); + + // Should still return true for invalid handle. + EXPECT_TRUE(context.ClaimAssociatedEndpointHandle( + AssociatedEndpointHandle_Data(internal::kEncodedInvalidHandleValue))); + } + + { + // Test the case that |num_associated_endpoint_handles| is the same value as + // |internal::kEncodedInvalidHandleValue|. + EXPECT_EQ(internal::kEncodedInvalidHandleValue, + std::numeric_limits<uint32_t>::max()); + internal::ValidationContext context(ToPtr(0), 0, 0, + std::numeric_limits<uint32_t>::max()); + + EXPECT_TRUE( + context.ClaimAssociatedEndpointHandle(AssociatedEndpointHandle_Data( + std::numeric_limits<uint32_t>::max() - 1))); + EXPECT_FALSE( + context.ClaimAssociatedEndpointHandle(AssociatedEndpointHandle_Data( + std::numeric_limits<uint32_t>::max() - 1))); + EXPECT_FALSE(context.ClaimAssociatedEndpointHandle( + AssociatedEndpointHandle_Data(0))); + + // Should still return true for invalid handle. + EXPECT_TRUE(context.ClaimAssociatedEndpointHandle( + AssociatedEndpointHandle_Data(internal::kEncodedInvalidHandleValue))); + } +} + TEST(ValidationContextTest, ClaimMemory) { { - internal::ValidationContext context(ToPtr(1000), 2000, 0); + internal::ValidationContext context(ToPtr(1000), 2000, 0, 0); // Basics. EXPECT_FALSE(context.ClaimMemory(ToPtr(500), 100)); @@ -186,7 +269,7 @@ { // No memory to claim. - internal::ValidationContext context(ToPtr(10000), 0, 0); + internal::ValidationContext context(ToPtr(10000), 0, 0, 0); EXPECT_FALSE(context.ClaimMemory(ToPtr(10000), 1)); EXPECT_FALSE(context.ClaimMemory(ToPtr(10000), 0)); @@ -194,7 +277,7 @@ { internal::ValidationContext context( - ToPtr(std::numeric_limits<uintptr_t>::max() - 1000), 500, 0); + ToPtr(std::numeric_limits<uintptr_t>::max() - 1000), 500, 0, 0); // Test overflow. EXPECT_FALSE(context.ClaimMemory(
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_good.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_good.data index efac7ed..b797fea 100644 --- a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_good.data +++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_good.data
@@ -1,13 +1,26 @@ [dist4]message_header // num_bytes -[u4]0 // version +[u4]2 // version [u4]0 // interface ID [u4]0 // name [u4]0 // flags [u4]0 // padding +[u8]0 // request_id +[dist8]payload +[dist8]payload_interface_ids [anchr]message_header +[anchr]payload [dist4]method0_params // num_bytes [u4]0 // version -[u4]4 // associated interface pointer: interface ID +[u4]1 // associated interface pointer: interface ID index [u4]1 // associated interface pointer: version [anchr]method0_params + +[anchr]payload_interface_ids +[dist4]interface_id_array // num_bytes +[u4]3 // num_elements : It is okay to have IDs that are not + // referred to. +[u4]4 +[u4]5 +[u4]8 +[anchr]interface_id_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_invalid_interface_id.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_invalid_interface_id.data new file mode 100644 index 0000000..a36d807 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_invalid_interface_id.data
@@ -0,0 +1,24 @@ +[dist4]message_header // num_bytes +[u4]2 // version +[u4]0 // interface ID +[u4]0 // name +[u4]0 // flags +[u4]0 // padding +[u8]0 // request_id +[dist8]payload +[dist8]payload_interface_ids +[anchr]message_header + +[anchr]payload +[dist4]method0_params // num_bytes +[u4]0 // version +[u4]1 // associated interface pointer: interface ID index +[u4]1 // associated interface pointer: version +[anchr]method0_params + +[anchr]payload_interface_ids +[dist4]interface_id_array // num_bytes +[u4]2 // num_elements +[u4]3 +[u4]0xFFFFFFFF // Unexpected invalid interface ID. +[anchr]interface_id_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_invalid_interface_id.expected similarity index 100% copy from mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.expected copy to mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_invalid_interface_id.expected
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_master_interface_id.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_master_interface_id.data new file mode 100644 index 0000000..e3fa5bb4 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_master_interface_id.data
@@ -0,0 +1,24 @@ +[dist4]message_header // num_bytes +[u4]2 // version +[u4]0 // interface ID +[u4]0 // name +[u4]0 // flags +[u4]0 // padding +[u8]0 // request_id +[dist8]payload +[dist8]payload_interface_ids +[anchr]message_header + +[anchr]payload +[dist4]method0_params // num_bytes +[u4]0 // version +[u4]1 // associated interface pointer: interface ID index +[u4]1 // associated interface pointer: version +[anchr]method0_params + +[anchr]payload_interface_ids +[dist4]interface_id_array // num_bytes +[u4]2 // num_elements +[u4]3 +[u4]0 // Unexpected master interface ID. +[anchr]interface_id_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_master_interface_id.expected similarity index 100% rename from mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.expected rename to mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_master_interface_id.expected
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_interface_id_index_out_of_range.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_interface_id_index_out_of_range.data new file mode 100644 index 0000000..f9e62015 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_interface_id_index_out_of_range.data
@@ -0,0 +1,27 @@ +[dist4]message_header // num_bytes +[u4]2 // version +[u4]0 // interface ID +[u4]0 // name +[u4]0 // flags +[u4]0 // padding +[u8]0 // request_id +[dist8]payload +[dist8]payload_interface_ids +[anchr]message_header + +[anchr]payload +[dist4]method0_params // num_bytes +[u4]0 // version +[u4]1111 // associated interface pointer: The interface ID index + // is out of range. +[u4]1 // associated interface pointer: version +[anchr]method0_params + +[anchr]payload_interface_ids +[dist4]interface_id_array // num_bytes +[u4]3 // num_elements : It is okay to have IDs that are not + // referred to. +[u4]4 +[u4]5 +[u4]8 +[anchr]interface_id_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_interface_id_index_out_of_range.expected similarity index 100% copy from mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.expected copy to mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_interface_id_index_out_of_range.expected
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_unexpected_invalid_associated_interface.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_unexpected_invalid_associated_interface.data index dd16401..b785ed1 100644 --- a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_unexpected_invalid_associated_interface.data +++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_unexpected_invalid_associated_interface.data
@@ -1,14 +1,25 @@ [dist4]message_header // num_bytes -[u4]0 // version +[u4]2 // version [u4]0 // interface ID [u4]0 // name [u4]0 // flags [u4]0 // padding +[u8]0 // request_id +[dist8]payload +[dist8]payload_interface_ids [anchr]message_header +[anchr]payload [dist4]method0_params // num_bytes [u4]0 // version -[u4]0xFFFFFFFF // associated interface pointer: unexpected invalid - // interface ID +[u4]0xFFFFFFFF // associated interface pointer: Unexpected invalid + // interface ID index. [u4]1 // associated interface pointer: version [anchr]method0_params + +[anchr]payload_interface_ids +[dist4]interface_id_array // num_bytes +[u4]2 // num_elements +[u4]3 +[u4]4 +[anchr]interface_id_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_good.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_good.data index 7ebbe07..efa2162 100644 --- a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_good.data +++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_good.data
@@ -1,13 +1,26 @@ [dist4]message_header // num_bytes -[u4]0 // version +[u4]2 // version [u4]0 // interface ID [u4]1 // name [u4]0 // flags [u4]0 // padding +[u8]0 // request_id +[dist8]payload +[dist8]payload_interface_ids [anchr]message_header +[anchr]payload [dist4]method1_params // num_bytes [u4]0 // version -[u4]4 // associated interface request +[u4]1 // associated interface request: interface ID index [u4]0 // padding [anchr]method1_params + +[anchr]payload_interface_ids +[dist4]interface_id_array // num_bytes +[u4]3 // num_elements : It is okay to have IDs that are not + // referred to. +[u4]4 +[u4]5 +[u4]8 +[anchr]interface_id_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_unexpected_invalid_associated_request.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_unexpected_invalid_associated_request.data index d74a306b..5a66aad8 100644 --- a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_unexpected_invalid_associated_request.data +++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_unexpected_invalid_associated_request.data
@@ -1,14 +1,27 @@ [dist4]message_header // num_bytes -[u4]0 // version +[u4]2 // version [u4]0 // interface ID [u4]1 // name [u4]0 // flags [u4]0 // padding +[u8]0 // request_id +[dist8]payload +[dist8]payload_interface_ids [anchr]message_header +[anchr]payload [dist4]method1_params // num_bytes [u4]0 // version -[u4]0xFFFFFFFF // associated interface request: unexpected invalid - // interface ID +[u4]0xFFFFFFFF // associated interface request: Unexpected invalid + // interface ID index. [u4]0 // padding [anchr]method1_params + +[anchr]payload_interface_ids +[dist4]interface_id_array // num_bytes +[u4]3 // num_elements : It is okay to have IDs that are not + // referred to. +[u4]4 +[u4]5 +[u4]8 +[anchr]interface_id_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_good.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_good.data new file mode 100644 index 0000000..ab29603 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_good.data
@@ -0,0 +1,18 @@ +[dist4]message_header // num_bytes +[u4]2 // version +[u4]0 // interface ID +[u4]2 // name +[u4]0 // flags +[u4]0 // padding +[u8]0 // request_id +[dist8]payload +[u8]0 // payload_interface_ids: This array is a nullable field. +[anchr]message_header + +[anchr]payload +[dist4]method2_params // num_bytes +[u4]0 // version +[u4]0xFFFFFFFF // associated interface pointer: Invalid interface ID + // index. +[u4]1 // associated interface pointer: version +[anchr]method2_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_good.expected new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_good.expected
@@ -0,0 +1 @@ +PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.data deleted file mode 100644 index 511cd0d5..0000000 --- a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.data +++ /dev/null
@@ -1,15 +0,0 @@ -[dist4]message_header // num_bytes -[u4]0 // version -[u4]0 // interface ID -[u4]2 // name -[u4]0 // flags -[u4]0 // padding -[anchr]message_header - -[dist4]method2_params // num_bytes -[u4]0 // version -[u4]0 // associated interface pointer: 0 is the special master - // interface ID and should never be used as associated - // interface ID -[u4]1 // associated interface pointer: version -[anchr]method2_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_collided_interface_id_indices.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_collided_interface_id_indices.data new file mode 100644 index 0000000..6cb71d3 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_collided_interface_id_indices.data
@@ -0,0 +1,36 @@ +[dist4]message_header // num_bytes +[u4]2 // version +[u4]0 // interface ID +[u4]3 // name +[u4]0 // flags +[u4]0 // padding +[u8]0 // request_id +[dist8]payload +[dist8]payload_interface_ids +[anchr]message_header + +[anchr]payload +[dist4]method3_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method3_params + +[anchr]param0_ptr +[dist4]associated_interface_array // num_bytes +[u4]2 // num_elements +[u4]2 // interface ID index +[u4]14 // version +[u4]2 // interface ID index: The same value as the + // one above. +[u4]18 // version +[anchr]associated_interface_array + +[anchr]payload_interface_ids +[dist4]interface_id_array // num_bytes +[u4]4 // num_elements : It is okay to have IDs that are not + // referred to. +[u4]4 +[u4]5 +[u4]8 +[u4]19 +[anchr]interface_id_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_collided_interface_id_indices.expected similarity index 100% copy from mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.expected copy to mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_collided_interface_id_indices.expected
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_good.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_good.data index da22db14..13df01e 100644 --- a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_good.data +++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_good.data
@@ -1,11 +1,15 @@ [dist4]message_header // num_bytes -[u4]0 // version +[u4]2 // version [u4]0 // interface ID [u4]3 // name [u4]0 // flags [u4]0 // padding +[u8]0 // request_id +[dist8]payload +[dist8]payload_interface_ids [anchr]message_header +[anchr]payload [dist4]method3_params // num_bytes [u4]0 // version [dist8]param0_ptr // param0 @@ -14,8 +18,18 @@ [anchr]param0_ptr [dist4]associated_interface_array // num_bytes [u4]2 // num_elements -[u4]4 // interface ID +[u4]2 // interface ID index [u4]14 // version -[u4]5 // interface ID +[u4]3 // interface ID index [u4]18 // version [anchr]associated_interface_array + +[anchr]payload_interface_ids +[dist4]interface_id_array // num_bytes +[u4]4 // num_elements : It is okay to have IDs that are not + // referred to. +[u4]4 +[u4]5 +[u4]8 +[u4]19 +[anchr]interface_id_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_unexpected_invalid_associated_interface_in_array.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_unexpected_invalid_associated_interface_in_array.data index 788cefa..2e163be 100644 --- a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_unexpected_invalid_associated_interface_in_array.data +++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_unexpected_invalid_associated_interface_in_array.data
@@ -1,11 +1,15 @@ [dist4]message_header // num_bytes -[u4]0 // version +[u4]2 // version [u4]0 // interface ID [u4]3 // name [u4]0 // flags [u4]0 // padding +[u8]0 // request_id +[dist8]payload +[dist8]payload_interface_ids [anchr]message_header +[anchr]payload [dist4]method3_params // num_bytes [u4]0 // version [dist8]param0_ptr // param0 @@ -14,8 +18,19 @@ [anchr]param0_ptr [dist4]associated_interface_array // num_bytes [u4]2 // num_elements -[u4]0xFFFFFFFF // unexpected invalid interface ID +[u4]2 // interface ID index [u4]14 // version -[u4]5 // interface ID +[u4]0xFFFFFFFF // interface ID index: Unexpected invalid + // value. [u4]18 // version [anchr]associated_interface_array + +[anchr]payload_interface_ids +[dist4]interface_id_array // num_bytes +[u4]4 // num_elements : It is okay to have IDs that are not + // referred to. +[u4]4 +[u4]5 +[u4]8 +[u4]19 +[anchr]interface_id_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_wrong_interface_id_index_order.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_wrong_interface_id_index_order.data new file mode 100644 index 0000000..4a63003 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_wrong_interface_id_index_order.data
@@ -0,0 +1,38 @@ +[dist4]message_header // num_bytes +[u4]2 // version +[u4]0 // interface ID +[u4]3 // name +[u4]0 // flags +[u4]0 // padding +[u8]0 // request_id +[dist8]payload +[dist8]payload_interface_ids +[anchr]message_header + +[anchr]payload +[dist4]method3_params // num_bytes +[u4]0 // version +[dist8]param0_ptr // param0 +[anchr]method3_params + +[anchr]param0_ptr +[dist4]associated_interface_array // num_bytes +[u4]3 // num_elements +[u4]2 // interface ID index +[u4]14 // version +[u4]3 // interface ID index +[u4]0 // version +[u4]0 // interface ID index : It is smaller than + // the first element above, which is wrong. +[u4]18 // version +[anchr]associated_interface_array + +[anchr]payload_interface_ids +[dist4]interface_id_array // num_bytes +[u4]4 // num_elements : It is okay to have IDs that are not + // referred to. +[u4]4 +[u4]5 +[u4]8 +[u4]19 +[anchr]interface_id_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_wrong_interface_id_index_order.expected similarity index 100% copy from mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.expected copy to mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_wrong_interface_id_index_order.expected
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl index 97044ca..a23b107 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
@@ -5,15 +5,16 @@ {%- set proxy_name = interface.name ~ "Proxy" %} {%- set namespace_as_string = "%s"|format(namespace|replace(".","::")) %} -{%- macro alloc_params(struct, params, message, serialization_context, - description) %} - ({{serialization_context}})->handles.Swap(({{message}})->mutable_handles()); +{%- macro alloc_params(struct, params, message, description) %} + mojo::internal::SerializationContext serialization_context; + serialization_context.handles.Swap(({{message}})->mutable_handles()); + serialization_context.associated_endpoint_handles.swap( + *({{message}})->mutable_associated_endpoint_handles()); bool success = true; {%- for param in struct.packed.packed_fields_in_ordinal_order %} {{param.field.kind|cpp_wrapper_type}} p_{{param.field.name}}{}; {%- endfor %} - {{struct.name}}DataView input_data_view({{params}}, - {{serialization_context}}); + {{struct.name}}DataView input_data_view({{params}}, &serialization_context); {{struct_macros.deserialize(struct, "input_data_view", "p_%s", "success")}} if (!success) { ReportValidationErrorForMessage( @@ -38,6 +39,8 @@ serialization_context)}} ({{serialization_context}})->handles.Swap( builder.message()->mutable_handles()); + ({{serialization_context}})->associated_endpoint_handles.swap( + *builder.message()->mutable_associated_endpoint_handles()); {%- endmacro %} {#--- Begin #} @@ -68,12 +71,11 @@ : public mojo::MessageReceiver { public: {{class_name}}_{{method.name}}_HandleSyncResponse( - scoped_refptr<mojo::AssociatedGroupController> group_controller, bool* result {%- for param in method.response_parameters -%} , {{param.kind|cpp_wrapper_type}}* out_{{param.name}} {%- endfor %}) - : serialization_context_(std::move(group_controller)), result_(result) + : result_(result) {%- for param in method.response_parameters -%} , out_{{param.name}}_(out_{{param.name}}) {%- endfor %} { @@ -81,7 +83,6 @@ } bool Accept(mojo::Message* message) override; private: - mojo::internal::SerializationContext serialization_context_; bool* result_; {%- for param in method.response_parameters %} {{param.kind|cpp_wrapper_type}}* out_{{param.name}}_; @@ -95,8 +96,7 @@ message->mutable_payload()); {%- set desc = class_name~"::"~method.name~" response" %} - {{alloc_params(method.response_param_struct, "params", "message", - "&serialization_context_", desc)}} + {{alloc_params(method.response_param_struct, "params", "message", desc)}} {%- for param in method.response_parameters %} *out_{{param.name}}_ = std::move(p_{{param.name}}); @@ -113,18 +113,15 @@ public: {{class_name}}_{{method.name}}_ForwardToCallback( {%- if use_once_callback %} - {{class_name}}::{{method.name}}Callback callback, + {{class_name}}::{{method.name}}Callback callback {%- else %} - const {{class_name}}::{{method.name}}Callback& callback, + const {{class_name}}::{{method.name}}Callback& callback {%- endif %} - scoped_refptr<mojo::AssociatedGroupController> group_controller) - : callback_(std::move(callback)), - serialization_context_(std::move(group_controller)) { + ) : callback_(std::move(callback)) { } bool Accept(mojo::Message* message) override; private: {{class_name}}::{{method.name}}Callback callback_; - mojo::internal::SerializationContext serialization_context_; DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ForwardToCallback); }; bool {{class_name}}_{{method.name}}_ForwardToCallback::Accept( @@ -134,8 +131,7 @@ message->mutable_payload()); {%- set desc = class_name~"::"~method.name~" response" %} - {{alloc_params(method.response_param_struct, "params", "message", - "&serialization_context_", desc)}} + {{alloc_params(method.response_param_struct, "params", "message", desc)}} if (!callback_.is_null()) { mojo::internal::MessageDispatchContext context(message); std::move(callback_).Run({{pass_params(method.response_parameters)}}); @@ -160,13 +156,14 @@ {%- if method.sync %} bool {{proxy_name}}::{{method.name}}( {{interface_macros.declare_sync_method_params("param_", method)}}) { - mojo::internal::SerializationContext serialization_context( - group_controller_); + mojo::internal::SerializationContext serialization_context; {{struct_macros.get_serialized_size(params_struct, "param_%s", "&serialization_context")}} - mojo::internal::RequestMessageBuilder builder({{message_name}}, size, - mojo::Message::kFlagIsSync); + mojo::internal::MessageBuilder builder( + {{message_name}}, + mojo::Message::kFlagIsSync | mojo::Message::kFlagExpectsResponse, + size, serialization_context.associated_endpoint_count); {{build_message(params_struct, "param_%s", params_description, "&serialization_context")}} @@ -174,7 +171,7 @@ bool result = false; mojo::MessageReceiver* responder = new {{class_name}}_{{method.name}}_HandleSyncResponse( - group_controller_, &result + &result {%- for param in method.response_parameters -%} , param_{{param.name}} {%- endfor %}); @@ -186,24 +183,25 @@ void {{proxy_name}}::{{method.name}}( {{interface_macros.declare_request_params("in_", method, use_once_callback)}}) { - mojo::internal::SerializationContext serialization_context( - group_controller_); + mojo::internal::SerializationContext serialization_context; {{struct_macros.get_serialized_size(params_struct, "in_%s", "&serialization_context")}} {%- if method.response_parameters != None %} - mojo::internal::RequestMessageBuilder builder({{message_name}}, size); + constexpr uint32_t kFlags = mojo::Message::kFlagExpectsResponse; {%- else %} - mojo::internal::MessageBuilder builder({{message_name}}, size); + constexpr uint32_t kFlags = 0; {%- endif %} + mojo::internal::MessageBuilder builder( + {{message_name}}, kFlags, size, + serialization_context.associated_endpoint_count); {{build_message(params_struct, "in_%s", params_description, "&serialization_context")}} {%- if method.response_parameters != None %} mojo::MessageReceiver* responder = - new {{class_name}}_{{method.name}}_ForwardToCallback( - std::move(callback), group_controller_); + new {{class_name}}_{{method.name}}_ForwardToCallback(std::move(callback)); if (!receiver_->AcceptWithResponder(builder.message(), responder)) delete responder; {%- else %} @@ -228,12 +226,10 @@ static {{class_name}}::{{method.name}}Callback CreateCallback( uint64_t request_id, bool is_sync, - mojo::MessageReceiverWithStatus* responder, - scoped_refptr<mojo::AssociatedGroupController> - group_controller) { + mojo::MessageReceiverWithStatus* responder) { std::unique_ptr<{{class_name}}_{{method.name}}_ProxyToResponder> proxy( new {{class_name}}_{{method.name}}_ProxyToResponder( - request_id, is_sync, responder, group_controller)); + request_id, is_sync, responder)); return base::Bind(&{{class_name}}_{{method.name}}_ProxyToResponder::Run, base::Passed(&proxy)); } @@ -256,12 +252,10 @@ {{class_name}}_{{method.name}}_ProxyToResponder( uint64_t request_id, bool is_sync, - mojo::MessageReceiverWithStatus* responder, - scoped_refptr<mojo::AssociatedGroupController> group_controller) + mojo::MessageReceiverWithStatus* responder) : request_id_(request_id), is_sync_(is_sync), - responder_(responder), - serialization_context_(std::move(group_controller)) { + responder_(responder) { } void Run( @@ -271,8 +265,6 @@ uint64_t request_id_; bool is_sync_; mojo::MessageReceiverWithStatus* responder_; - // TODO(yzshen): maybe I should use a ref to the original one? - mojo::internal::SerializationContext serialization_context_; DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ProxyToResponder); }; @@ -280,13 +272,19 @@ void {{class_name}}_{{method.name}}_ProxyToResponder::Run( {{interface_macros.declare_responder_params( "in_", method.response_parameters, for_blink)}}) { + mojo::internal::SerializationContext serialization_context; {{struct_macros.get_serialized_size(response_params_struct, "in_%s", - "&serialization_context_")}} - mojo::internal::ResponseMessageBuilder builder( - {{message_name}}, size, request_id_, - is_sync_ ? mojo::Message::kFlagIsSync : 0); + "&serialization_context")}} + + uint32_t flags = (is_sync_ ? mojo::Message::kFlagIsSync : 0) | + mojo::Message::kFlagIsResponse; + mojo::internal::MessageBuilder builder( + {{message_name}}, flags, size, + serialization_context.associated_endpoint_count); + builder.message()->set_request_id(request_id_); + {{build_message(response_params_struct, "in_%s", params_description, - "&serialization_context_")}} + "&serialization_context")}} bool ok = responder_->Accept(builder.message()); ALLOW_UNUSED_LOCAL(ok); // TODO(darin): !ok returned here indicates a malformed message, and that may @@ -303,7 +301,6 @@ // static bool {{class_name}}StubDispatch::Accept( {{interface.name}}* impl, - mojo::internal::SerializationContext* context, mojo::Message* message) { {%- if interface.methods %} switch (message->header()->name) { @@ -315,7 +312,7 @@ message->mutable_payload()); {%- set desc = class_name~"::"~method.name %} - {{alloc_params(method.param_struct, "params", "message", "context", desc)| + {{alloc_params(method.param_struct, "params", "message", desc)| indent(4)}} // A null |impl| means no implementation was bound. assert(impl); @@ -336,7 +333,6 @@ // static bool {{class_name}}StubDispatch::AcceptWithResponder( {{interface.name}}* impl, - mojo::internal::SerializationContext* context, mojo::Message* message, mojo::MessageReceiverWithStatus* responder) { {%- if interface.methods %} @@ -349,13 +345,12 @@ message->mutable_payload()); {%- set desc = class_name~"::"~method.name %} - {{alloc_params(method.param_struct, "params", "message", "context", desc)| + {{alloc_params(method.param_struct, "params", "message", desc)| indent(4)}} {{class_name}}::{{method.name}}Callback callback = {{class_name}}_{{method.name}}_ProxyToResponder::CreateCallback( message->request_id(), - message->has_flag(mojo::Message::kFlagIsSync), responder, - context->group_controller); + message->has_flag(mojo::Message::kFlagIsSync), responder); // A null |impl| means no implementation was bound. assert(impl); TRACE_EVENT0("mojom", "{{class_name}}::{{method.name}}"); @@ -380,8 +375,9 @@ return true; mojo::internal::ValidationContext validation_context( - message->data(), message->data_num_bytes(), message->handles()->size(), - message, "{{class_name}} RequestValidator"); + message->payload(), message->payload_num_bytes(), + message->handles()->size(), message->payload_num_interface_ids(), message, + "{{class_name}} RequestValidator"); switch (message->header()->name) { {%- for method in interface.methods %} @@ -423,8 +419,9 @@ return true; mojo::internal::ValidationContext validation_context( - message->data(), message->data_num_bytes(), message->handles()->size(), - message, "{{class_name}} ResponseValidator"); + message->payload(), message->payload_num_bytes(), + message->handles()->size(), message->payload_num_interface_ids(), message, + "{{class_name}} ResponseValidator"); if (!mojo::internal::ValidateMessageIsResponse(message, &validation_context)) return false;
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl index ff2a1dee..0a158ec 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl
@@ -11,11 +11,6 @@ void {{method.name}}({{interface_macros.declare_request_params("", method, use_once_callback)}}) override; {%- endfor %} - void set_group_controller( - scoped_refptr<mojo::AssociatedGroupController> group_controller) { - group_controller_ = std::move(group_controller); - } private: mojo::MessageReceiverWithResponder* receiver_; - scoped_refptr<mojo::AssociatedGroupController> group_controller_; };
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl index a8296b71..9f01348 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl
@@ -1,10 +1,7 @@ class {{export_attribute}} {{interface.name}}StubDispatch { public: - static bool Accept({{interface.name}}* impl, - mojo::internal::SerializationContext* context, - mojo::Message* message); + static bool Accept({{interface.name}}* impl, mojo::Message* message); static bool AcceptWithResponder({{interface.name}}* impl, - mojo::internal::SerializationContext* context, mojo::Message* message, mojo::MessageReceiverWithStatus* responder); }; @@ -22,15 +19,11 @@ void set_sink(ImplPointerType sink) { sink_ = std::move(sink); } ImplPointerType& sink() { return sink_; } - mojo::internal::SerializationContext* serialization_context() { - return &serialization_context_; - } - bool Accept(mojo::Message* message) override { if (ImplRefTraits::IsNull(sink_)) return false; return {{interface.name}}StubDispatch::Accept( - ImplRefTraits::GetRawPointer(&sink_), &serialization_context_, message); + ImplRefTraits::GetRawPointer(&sink_), message); } bool AcceptWithResponder( @@ -39,11 +32,9 @@ if (ImplRefTraits::IsNull(sink_)) return false; return {{interface.name}}StubDispatch::AcceptWithResponder( - ImplRefTraits::GetRawPointer(&sink_), &serialization_context_, message, - responder); + ImplRefTraits::GetRawPointer(&sink_), message, responder); } private: ImplPointerType sink_; - mojo::internal::SerializationContext serialization_context_; };
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl index fbf60dd..bb5fb9c 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl
@@ -19,7 +19,8 @@ {%- macro get_serialized_size(struct, input_field_pattern, context, input_may_be_temp=False) -%} size_t size = sizeof({{struct|get_qualified_name_for_kind(internal=True)}}); -{%- for pf in struct.packed.packed_fields_in_ordinal_order if pf.field.kind|is_object_kind %} +{%- for pf in struct.packed.packed_fields_in_ordinal_order + if pf.field.kind|is_object_kind or pf.field.kind|is_associated_kind %} {%- set name = pf.field.name -%} {%- set kind = pf.field.kind -%} {%- set original_input_field = input_field_pattern|format(name) %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl index 9a1aa8e..b589ae91 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl
@@ -23,7 +23,7 @@ {%- for field in union.fields %} {%- set name = field.name %} case {{data_view}}::Tag::{{name|upper}}: { -{%- if field.kind|is_object_kind %} +{%- if field.kind|is_object_kind or field.kind|is_associated_kind %} {%- set kind = field.kind %} {%- set serializer_type = kind|unmapped_type_for_serializer %} decltype(CallWithContext(Traits::{{name}}, input, custom_context))
diff --git a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py index be70d2f..e66bec3 100644 --- a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py +++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
@@ -342,7 +342,7 @@ if mojom.IsAssociatedInterfaceKind(kind): return "mojo::internal::AssociatedInterface_Data" if mojom.IsAssociatedInterfaceRequestKind(kind): - return "mojo::internal::AssociatedInterfaceRequest_Data" + return "mojo::internal::AssociatedEndpointHandle_Data" if mojom.IsEnumKind(kind): return "int32_t" if mojom.IsStringKind(kind):
diff --git a/net/BUILD.gn b/net/BUILD.gn index b3afa09..8a7ee89e 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn
@@ -4181,6 +4181,7 @@ "dns/dns_query_unittest.cc", "dns/dns_response_unittest.cc", "dns/dns_session_unittest.cc", + "dns/dns_socket_pool_unittest.cc", "dns/dns_transaction_unittest.cc", "dns/dns_util_unittest.cc", "dns/host_cache_unittest.cc",
diff --git a/net/base/mime_util.cc b/net/base/mime_util.cc index 8a83417..4e94fa5 100644 --- a/net/base/mime_util.cc +++ b/net/base/mime_util.cc
@@ -459,16 +459,30 @@ { NULL, NULL, 0 } }; +// GetExtensionsFromHardCodedMappings() adds file extensions (without a leading +// dot) to the set |extensions|, for all MIME types matching |mime_type|. +// +// The meaning of |mime_type| depends on the value of |prefix_match|: +// +// * If |prefix_match = false| then |mime_type| is an exact (case-insensitive) +// string such as "text/plain". +// +// * If |prefix_match = true| then |mime_type| is treated as the prefix for a +// (case-insensitive) string. For instance "Text/" would match "text/plain". +template <size_t N> void GetExtensionsFromHardCodedMappings( - const MimeInfo* mappings, - size_t mappings_len, - const std::string& leading_mime_type, + const MimeInfo (&mappings)[N], + const std::string& mime_type, + bool prefix_match, std::unordered_set<base::FilePath::StringType>* extensions) { - for (size_t i = 0; i < mappings_len; ++i) { - if (base::StartsWith(mappings[i].mime_type, leading_mime_type, - base::CompareCase::INSENSITIVE_ASCII)) { + for (const auto& mapping : mappings) { + base::StringPiece cur_mime_type(mapping.mime_type); + + if (base::StartsWith(cur_mime_type, mime_type, + base::CompareCase::INSENSITIVE_ASCII) && + (prefix_match || (cur_mime_type.length() == mime_type.length()))) { for (const base::StringPiece& this_extension : base::SplitStringPiece( - mappings[i].extensions, ",", base::TRIM_WHITESPACE, + mapping.extensions, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { #if defined(OS_WIN) extensions->insert(base::UTF8ToUTF16(this_extension)); @@ -492,13 +506,11 @@ // Also look up the extensions from hard-coded mappings in case that some // supported extensions are not registered in the system registry, like ogg. - GetExtensionsFromHardCodedMappings(kPrimaryMappings, - arraysize(kPrimaryMappings), - leading_mime_type, extensions); + GetExtensionsFromHardCodedMappings(kPrimaryMappings, leading_mime_type, true, + extensions); - GetExtensionsFromHardCodedMappings(kSecondaryMappings, - arraysize(kSecondaryMappings), - leading_mime_type, extensions); + GetExtensionsFromHardCodedMappings(kSecondaryMappings, leading_mime_type, + true, extensions); } // Note that the elements in the source set will be appended to the target @@ -561,12 +573,10 @@ // Also look up the extensions from hard-coded mappings in case that some // supported extensions are not registered in the system registry, like ogg. - GetExtensionsFromHardCodedMappings(kPrimaryMappings, - arraysize(kPrimaryMappings), mime_type, + GetExtensionsFromHardCodedMappings(kPrimaryMappings, mime_type, false, &unique_extensions); - GetExtensionsFromHardCodedMappings(kSecondaryMappings, - arraysize(kSecondaryMappings), mime_type, + GetExtensionsFromHardCodedMappings(kSecondaryMappings, mime_type, false, &unique_extensions); }
diff --git a/net/base/mime_util_unittest.cc b/net/base/mime_util_unittest.cc index 3d9296a1a..608f16d 100644 --- a/net/base/mime_util_unittest.cc +++ b/net/base/mime_util_unittest.cc
@@ -2,10 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "net/base/mime_util.h" + +#include <algorithm> + #include "base/strings/string_split.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" -#include "net/base/mime_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -236,42 +239,46 @@ const char* const mime_type; size_t min_expected_size; const char* const contained_result; + bool no_matches; } tests[] = { - { "text/plain", 2, "txt" }, - { "*", 0, NULL }, - { "message/*", 1, "eml" }, - { "MeSsAge/*", 1, "eml" }, - { "image/bmp", 1, "bmp" }, - { "video/*", 6, "mp4" }, + {"text/plain", 2, "txt"}, + {"text/pl", 0, NULL, true}, + {"*", 0, NULL}, + {"", 0, NULL, true}, + {"message/*", 1, "eml"}, + {"MeSsAge/*", 1, "eml"}, + {"message/", 0, NULL, true}, + {"image/bmp", 1, "bmp"}, + {"video/*", 6, "mp4"}, #if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_IOS) - { "video/*", 6, "mpg" }, + {"video/*", 6, "mpg"}, #else - { "video/*", 6, "mpeg" }, + {"video/*", 6, "mpeg"}, #endif - { "audio/*", 6, "oga" }, - { "aUDIo/*", 6, "wav" }, + {"audio/*", 6, "oga"}, + {"aUDIo/*", 6, "wav"}, }; - for (size_t i = 0; i < arraysize(tests); ++i) { + for (const auto& test : tests) { std::vector<base::FilePath::StringType> extensions; - GetExtensionsForMimeType(tests[i].mime_type, &extensions); - ASSERT_TRUE(tests[i].min_expected_size <= extensions.size()); + GetExtensionsForMimeType(test.mime_type, &extensions); + ASSERT_LE(test.min_expected_size, extensions.size()); - if (!tests[i].contained_result) - continue; + if (test.no_matches) + ASSERT_EQ(0u, extensions.size()); - bool found = false; - for (size_t j = 0; !found && j < extensions.size(); ++j) { -#if defined(OS_WIN) - if (extensions[j] == base::UTF8ToWide(tests[i].contained_result)) - found = true; -#else - if (extensions[j] == tests[i].contained_result) - found = true; -#endif + if (test.contained_result) { + // Convert ASCII to FilePath::StringType. + base::FilePath::StringType contained_result( + test.contained_result, + test.contained_result + strlen(test.contained_result)); + + bool found = std::find(extensions.begin(), extensions.end(), + contained_result) != extensions.end(); + + ASSERT_TRUE(found) << "Must find at least the contained result within " + << test.mime_type; } - ASSERT_TRUE(found) << "Must find at least the contained result within " - << tests[i].mime_type; } }
diff --git a/net/dns/dns_socket_pool_unittest.cc b/net/dns/dns_socket_pool_unittest.cc new file mode 100644 index 0000000..a77697f --- /dev/null +++ b/net/dns/dns_socket_pool_unittest.cc
@@ -0,0 +1,108 @@ +// 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 "net/dns/dns_socket_pool.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/memory/weak_ptr.h" +#include "net/base/rand_callback.h" +#include "net/socket/client_socket_factory.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace { + +class DummyObject { + public: + DummyObject() : weak_factory_(this) {} + + base::WeakPtr<DummyObject> GetWeakPtr() { return weak_factory_.GetWeakPtr(); } + + bool HasWeakPtrs() const { return weak_factory_.HasWeakPtrs(); } + + private: + base::WeakPtrFactory<DummyObject> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(DummyObject); +}; + +class DummyRandIntCallback { + public: + DummyRandIntCallback() {} + + RandIntCallback MakeCallback() { + return base::Bind(&DummyRandIntCallback::GetRandInt, dummy_.GetWeakPtr()); + } + + bool HasRefs() const { return dummy_.HasWeakPtrs(); } + + private: + static int GetRandInt(base::WeakPtr<DummyObject> dummy, int from, int to) { + // Chosen by fair dice roll. Guaranteed to be random. + return 4; + } + + DummyObject dummy_; + + DISALLOW_COPY_AND_ASSIGN(DummyRandIntCallback); +}; + +// Since the below tests rely upon it, make sure that DummyRandIntCallback +// can reliably tell whether there are other refs to the callback it returns. + +// A const reference to the callback shouldn't keep the callback referenced. +TEST(DummyRandIntCallbackTest, Referenced) { + DummyRandIntCallback dummy; + + RandIntCallback original = dummy.MakeCallback(); + EXPECT_TRUE(dummy.HasRefs()); + const RandIntCallback& reference = original; + EXPECT_TRUE(dummy.HasRefs()); + + EXPECT_EQ(4, reference.Run(0, 6)); + + original.Reset(); + EXPECT_FALSE(dummy.HasRefs()); +} + +// A copy of the callback should keep the callback referenced. +TEST(DummyRandIntCallbackTest, Copied) { + DummyRandIntCallback dummy; + + RandIntCallback original = dummy.MakeCallback(); + EXPECT_TRUE(dummy.HasRefs()); + RandIntCallback copy = original; + EXPECT_TRUE(dummy.HasRefs()); + + EXPECT_EQ(4, copy.Run(0, 6)); + + original.Reset(); + EXPECT_TRUE(dummy.HasRefs()); +} + +class DnsSocketPoolTest : public ::testing::Test { + protected: + DummyRandIntCallback dummy_; + std::unique_ptr<DnsSocketPool> pool_; +}; + +// Make sure that the DnsSocketPools returned by CreateDefault and CreateNull +// both retain (by copying the RandIntCallback object, instead of taking a +// reference) the RandIntCallback used for creating sockets. + +TEST_F(DnsSocketPoolTest, DefaultCopiesCallback) { + pool_ = DnsSocketPool::CreateDefault(ClientSocketFactory::GetDefaultFactory(), + dummy_.MakeCallback()); + EXPECT_TRUE(dummy_.HasRefs()); +} + +TEST_F(DnsSocketPoolTest, NullCopiesCallback) { + pool_ = DnsSocketPool::CreateNull(ClientSocketFactory::GetDefaultFactory(), + dummy_.MakeCallback()); + EXPECT_TRUE(dummy_.HasRefs()); +} + +} // namespace +} // namespace net
diff --git a/net/http/http_network_transaction_ssl_unittest.cc b/net/http/http_network_transaction_ssl_unittest.cc index 5ad359b..f3e027d 100644 --- a/net/http/http_network_transaction_ssl_unittest.cc +++ b/net/http/http_network_transaction_ssl_unittest.cc
@@ -9,9 +9,10 @@ #include "base/deferred_sequenced_task_runner.h" #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/test/scoped_task_scheduler.h" #include "base/threading/thread.h" -#include "base/threading/thread_task_runner_handle.h" #include "net/base/request_priority.h" #include "net/cert/ct_policy_enforcer.h" #include "net/cert/mock_cert_verifier.h" @@ -56,6 +57,9 @@ class HttpNetworkTransactionSSLTest : public testing::Test { protected: + HttpNetworkTransactionSSLTest() + : scoped_task_scheduler_(base::MessageLoop::current()) {} + void SetUp() override { ssl_config_service_ = new TokenBindingSSLConfigService; session_params_.ssl_config_service = ssl_config_service_.get(); @@ -83,6 +87,8 @@ return request_info; } + base::test::ScopedTaskScheduler scoped_task_scheduler_; + scoped_refptr<SSLConfigService> ssl_config_service_; std::unique_ptr<HttpAuthHandlerMock::Factory> auth_handler_factory_; std::unique_ptr<ProxyService> proxy_service_; @@ -100,8 +106,7 @@ #if !defined(OS_IOS) TEST_F(HttpNetworkTransactionSSLTest, TokenBinding) { - ChannelIDService channel_id_service(new DefaultChannelIDStore(NULL), - base::ThreadTaskRunnerHandle::Get()); + ChannelIDService channel_id_service(new DefaultChannelIDStore(NULL)); session_params_.channel_id_service = &channel_id_service; SSLSocketDataProvider ssl_data(ASYNC, OK); @@ -150,8 +155,7 @@ } TEST_F(HttpNetworkTransactionSSLTest, NoTokenBindingOverHttp) { - ChannelIDService channel_id_service(new DefaultChannelIDStore(NULL), - base::ThreadTaskRunnerHandle::Get()); + ChannelIDService channel_id_service(new DefaultChannelIDStore(NULL)); session_params_.channel_id_service = &channel_id_service; SSLSocketDataProvider ssl_data(ASYNC, OK); @@ -187,8 +191,8 @@ channel_id_thread.Start(); scoped_refptr<base::DeferredSequencedTaskRunner> channel_id_runner = new base::DeferredSequencedTaskRunner(channel_id_thread.task_runner()); - ChannelIDService channel_id_service(new DefaultChannelIDStore(nullptr), - channel_id_runner); + ChannelIDService channel_id_service(new DefaultChannelIDStore(nullptr)); + channel_id_service.set_task_runner_for_testing(channel_id_runner); session_params_.channel_id_service = &channel_id_service; SSLSocketDataProvider ssl_data(ASYNC, OK);
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index 2117ba50..effbf45f 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc
@@ -23,9 +23,11 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/memory/weak_ptr.h" +#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_task_scheduler.h" #include "base/test/test_file_util.h" #include "base/threading/thread_task_runner_handle.h" #include "net/base/auth.h" @@ -16484,6 +16486,10 @@ #if !defined(OS_IOS) TEST_F(HttpNetworkTransactionTest, TokenBindingSpdy) { + // Required by ChannelIDService. + base::test::ScopedTaskScheduler scoped_task_scheduler( + base::MessageLoop::current()); + const std::string https_url = "https://www.example.com"; HttpRequestInfo request; request.url = GURL(https_url); @@ -16501,8 +16507,8 @@ MockRead(ASYNC, ERR_IO_PENDING)}; StaticSocketDataProvider data(reads, arraysize(reads), nullptr, 0); session_deps_.socket_factory->AddSocketDataProvider(&data); - session_deps_.channel_id_service.reset(new ChannelIDService( - new DefaultChannelIDStore(nullptr), base::ThreadTaskRunnerHandle::Get())); + session_deps_.channel_id_service.reset( + new ChannelIDService(new DefaultChannelIDStore(nullptr))); std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_)); HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
diff --git a/net/proxy/OWNERS b/net/proxy/OWNERS index bb65116..4c98734 100644 --- a/net/proxy/OWNERS +++ b/net/proxy/OWNERS
@@ -1,2 +1,4 @@ per-file *_struct_traits*.*=set noparent per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS + +per-file *_v8*=jochen@chromium.org
diff --git a/net/quic/chromium/quic_end_to_end_unittest.cc b/net/quic/chromium/quic_end_to_end_unittest.cc index 0df6b3cd..867d2ff 100644 --- a/net/quic/chromium/quic_end_to_end_unittest.cc +++ b/net/quic/chromium/quic_end_to_end_unittest.cc
@@ -9,9 +9,10 @@ #include "base/compiler_specific.h" #include "base/memory/ptr_util.h" +#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" -#include "base/threading/thread_task_runner_handle.h" +#include "base/test/scoped_task_scheduler.h" #include "net/base/elements_upload_data_stream.h" #include "net/base/ip_address.h" #include "net/base/test_completion_callback.h" @@ -126,8 +127,7 @@ params_.http_auth_handler_factory = auth_handler_factory_.get(); params_.http_server_properties = &http_server_properties_; channel_id_service_.reset( - new ChannelIDService(new DefaultChannelIDStore(nullptr), - base::ThreadTaskRunnerHandle::Get())); + new ChannelIDService(new DefaultChannelIDStore(nullptr))); params_.channel_id_service = channel_id_service_.get(); CertVerifyResult verify_result; @@ -278,6 +278,10 @@ } TEST_P(QuicEndToEndTest, TokenBinding) { + // Required by ChannelIDService. + base::test::ScopedTaskScheduler scoped_task_scheduler( + base::MessageLoop::current()); + // Enable token binding and re-initialize the TestTransactionFactory. params_.enable_token_binding = true; transaction_factory_.reset(new TestTransactionFactory(params_));
diff --git a/net/quic/chromium/quic_stream_factory_test.cc b/net/quic/chromium/quic_stream_factory_test.cc index a0ba4c1..e40a2bd 100644 --- a/net/quic/chromium/quic_stream_factory_test.cc +++ b/net/quic/chromium/quic_stream_factory_test.cc
@@ -13,7 +13,6 @@ #include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "base/strings/string_util.h" -#include "base/threading/thread_task_runner_handle.h" #include "net/base/test_proxy_delegate.h" #include "net/cert/cert_verifier.h" #include "net/cert/ct_policy_enforcer.h" @@ -229,8 +228,7 @@ Perspective::IS_SERVER), cert_verifier_(CertVerifier::CreateDefault()), channel_id_service_( - new ChannelIDService(new DefaultChannelIDStore(nullptr), - base::ThreadTaskRunnerHandle::Get())), + new ChannelIDService(new DefaultChannelIDStore(nullptr))), cert_transparency_verifier_(new MultiLogCTVerifier()), scoped_mock_network_change_notifier_(nullptr), factory_(nullptr),
diff --git a/net/quic/core/quic_headers_stream_test.cc b/net/quic/core/quic_headers_stream_test.cc index 09f0617..33dcaeaa 100644 --- a/net/quic/core/quic_headers_stream_test.cc +++ b/net/quic/core/quic_headers_stream_test.cc
@@ -109,7 +109,8 @@ SpdyStreamId parent_stream_id, int weight, bool exclusive)); - MOCK_METHOD2(OnUnknownFrame, bool(SpdyStreamId stream_id, int frame_type)); + MOCK_METHOD2(OnUnknownFrame, + bool(SpdyStreamId stream_id, uint8_t frame_type)); }; class ForceHolAckListener : public QuicAckListenerInterface { @@ -352,7 +353,7 @@ } framer_->ProcessInput(saved_data_.data(), saved_data_.length()); EXPECT_FALSE(framer_->HasError()) - << SpdyFramer::ErrorCodeToString(framer_->error_code()); + << SpdyFramer::SpdyFramerErrorToString(framer_->spdy_framer_error()); CheckHeaders(); saved_data_.clear(); @@ -455,7 +456,7 @@ EXPECT_CALL(visitor_, OnHeaderFrameEnd(stream_id, true)).Times(1); framer_->ProcessInput(saved_data_.data(), saved_data_.length()); EXPECT_FALSE(framer_->HasError()) - << SpdyFramer::ErrorCodeToString(framer_->error_code()); + << SpdyFramer::SpdyFramerErrorToString(framer_->spdy_framer_error()); CheckHeaders(); saved_data_.clear(); } else {
diff --git a/net/quic/core/quic_spdy_session.cc b/net/quic/core/quic_spdy_session.cc index 279646b..b88b547 100644 --- a/net/quic/core/quic_spdy_session.cc +++ b/net/quic/core/quic_spdy_session.cc
@@ -4,6 +4,9 @@ #include "net/quic/core/quic_spdy_session.h" +#include <algorithm> +#include <cstdint> +#include <string> #include <utility> #include "net/quic/core/quic_flags.h" @@ -135,9 +138,9 @@ } void OnError(SpdyFramer* framer) override { - CloseConnection( - QuicStrCat("SPDY framing error: ", - SpdyFramer::ErrorCodeToString(framer->error_code()))); + CloseConnection(QuicStrCat( + "SPDY framing error: ", + SpdyFramer::SpdyFramerErrorToString(framer->spdy_framer_error()))); } void OnDataFrameHeader(SpdyStreamId stream_id, @@ -256,7 +259,7 @@ CloseConnection("SPDY PRIORITY frame received."); } - bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override { + bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override { CloseConnection("Unknown frame type received."); return false; }
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc index ec24dcb..3b1f266 100644 --- a/net/socket/ssl_client_socket_unittest.cc +++ b/net/socket/ssl_client_socket_unittest.cc
@@ -14,8 +14,10 @@ #include "base/location.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" +#include "base/test/scoped_task_scheduler.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "base/values.h" @@ -1010,25 +1012,29 @@ class SSLClientSocketChannelIDTest : public SSLClientSocketTest { protected: + SSLClientSocketChannelIDTest() + : scoped_task_scheduler_(base::MessageLoop::current()) {} + void EnableChannelID() { - channel_id_service_.reset(new ChannelIDService( - new DefaultChannelIDStore(NULL), base::ThreadTaskRunnerHandle::Get())); + channel_id_service_.reset( + new ChannelIDService(new DefaultChannelIDStore(NULL))); context_.channel_id_service = channel_id_service_.get(); } void EnableFailingChannelID() { - channel_id_service_.reset(new ChannelIDService( - new FailingChannelIDStore(), base::ThreadTaskRunnerHandle::Get())); + channel_id_service_.reset( + new ChannelIDService(new FailingChannelIDStore())); context_.channel_id_service = channel_id_service_.get(); } void EnableAsyncFailingChannelID() { - channel_id_service_.reset(new ChannelIDService( - new AsyncFailingChannelIDStore(), base::ThreadTaskRunnerHandle::Get())); + channel_id_service_.reset( + new ChannelIDService(new AsyncFailingChannelIDStore())); context_.channel_id_service = channel_id_service_.get(); } private: + base::test::ScopedTaskScheduler scoped_task_scheduler_; std::unique_ptr<ChannelIDService> channel_id_service_; };
diff --git a/net/spdy/buffered_spdy_framer.cc b/net/spdy/buffered_spdy_framer.cc index 19facae..fc58368 100644 --- a/net/spdy/buffered_spdy_framer.cc +++ b/net/spdy/buffered_spdy_framer.cc
@@ -42,7 +42,7 @@ void BufferedSpdyFramer::OnError(SpdyFramer* spdy_framer) { DCHECK(spdy_framer); - visitor_->OnError(spdy_framer->error_code()); + visitor_->OnError(spdy_framer->spdy_framer_error()); } void BufferedSpdyFramer::OnHeaders(SpdyStreamId stream_id, @@ -203,7 +203,7 @@ } bool BufferedSpdyFramer::OnUnknownFrame(SpdyStreamId stream_id, - int frame_type) { + uint8_t frame_type) { return visitor_->OnUnknownFrame(stream_id, frame_type); } @@ -219,8 +219,8 @@ spdy_framer_.Reset(); } -SpdyFramer::SpdyError BufferedSpdyFramer::error_code() const { - return spdy_framer_.error_code(); +SpdyFramer::SpdyFramerError BufferedSpdyFramer::spdy_framer_error() const { + return spdy_framer_.spdy_framer_error(); } SpdyFramer::SpdyState BufferedSpdyFramer::state() const {
diff --git a/net/spdy/buffered_spdy_framer.h b/net/spdy/buffered_spdy_framer.h index 7412f589..4352e7f 100644 --- a/net/spdy/buffered_spdy_framer.h +++ b/net/spdy/buffered_spdy_framer.h
@@ -26,7 +26,7 @@ BufferedSpdyFramerVisitorInterface() {} // Called if an error is detected in the SpdySerializedFrame protocol. - virtual void OnError(SpdyFramer::SpdyError error_code) = 0; + virtual void OnError(SpdyFramer::SpdyFramerError spdy_framer_error) = 0; // Called if an error is detected in a HTTP2 stream. virtual void OnStreamError(SpdyStreamId stream_id, @@ -107,7 +107,7 @@ // Return true if this appears to be a valid extension frame, false otherwise. // We distinguish between extension frames and nonsense by checking // whether the stream id is valid. - virtual bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) = 0; + virtual bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) = 0; protected: virtual ~BufferedSpdyFramerVisitorInterface() {} @@ -171,13 +171,13 @@ size_t length, bool fin) override; void OnContinuation(SpdyStreamId stream_id, bool end) override; - bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override; + bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override; // SpdyFramer methods. size_t ProcessInput(const char* data, size_t len); void UpdateHeaderDecoderTableSize(uint32_t value); void Reset(); - SpdyFramer::SpdyError error_code() const; + SpdyFramer::SpdyFramerError spdy_framer_error() const; SpdyFramer::SpdyState state() const; bool MessageFullyRead(); bool HasError();
diff --git a/net/spdy/buffered_spdy_framer_unittest.cc b/net/spdy/buffered_spdy_framer_unittest.cc index a5bf1c1f..b0a4472 100644 --- a/net/spdy/buffered_spdy_framer_unittest.cc +++ b/net/spdy/buffered_spdy_framer_unittest.cc
@@ -28,8 +28,8 @@ header_stream_id_(static_cast<SpdyStreamId>(-1)), promised_stream_id_(static_cast<SpdyStreamId>(-1)) {} - void OnError(SpdyFramer::SpdyError error_code) override { - VLOG(1) << "SpdyFramer Error: " << error_code; + void OnError(SpdyFramer::SpdyFramerError spdy_framer_error) override { + VLOG(1) << "SpdyFramer Error: " << spdy_framer_error; error_count_++; } @@ -123,7 +123,7 @@ altsvc_vector_ = altsvc_vector; } - bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override { + bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override { return true; } @@ -133,7 +133,8 @@ size_t input_remaining = size; const char* input_ptr = reinterpret_cast<const char*>(input); while (input_remaining > 0 && - buffered_spdy_framer_.error_code() == SpdyFramer::SPDY_NO_ERROR) { + buffered_spdy_framer_.spdy_framer_error() == + SpdyFramer::SPDY_NO_ERROR) { // To make the tests more interesting, we feed random (amd small) chunks // into the framer. This simulates getting strange-sized reads from // the socket.
diff --git a/net/spdy/http2_frame_decoder_adapter.cc b/net/spdy/http2_frame_decoder_adapter.cc index 82efb59..4b45c8ef 100644 --- a/net/spdy/http2_frame_decoder_adapter.cc +++ b/net/spdy/http2_frame_decoder_adapter.cc
@@ -11,6 +11,7 @@ #include <stddef.h> +#include <cstdint> #include <cstring> #include <string> #include <utility> @@ -81,7 +82,7 @@ class Http2DecoderAdapter : public SpdyFramerDecoderAdapter, public Http2FrameDecoderListener { typedef SpdyFramer::SpdyState SpdyState; - typedef SpdyFramer::SpdyError SpdyError; + typedef SpdyFramer::SpdyFramerError SpdyFramerError; public: explicit Http2DecoderAdapter(SpdyFramer* outer_framer) @@ -118,7 +119,7 @@ // in an error state. DCHECK(processed > 0) << "processed=" << processed << " spdy_state_=" << spdy_state_ - << " spdy_error_=" << spdy_error_; + << " spdy_framer_error_=" << spdy_framer_error_; data += processed; len -= processed; @@ -134,7 +135,9 @@ SpdyState state() const override { return spdy_state_; } - SpdyError error_code() const override { return spdy_error_; } + SpdyFramerError spdy_framer_error() const override { + return spdy_framer_error_; + } bool probable_http_response() const override { return latched_probable_http_response_; @@ -158,7 +161,7 @@ // the rest of the control frame header is valid. // We rely on the visitor to check validity of stream_id. bool valid_stream = visitor()->OnUnknownFrame( - header.stream_id, static_cast<int>(header.type)); + header.stream_id, static_cast<uint8_t>(header.type)); if (has_expected_frame_type_ && header.type != expected_frame_type_) { // Report an unexpected frame error and close the connection if we // expect a known frame type (probably CONTINUATION) and receive an @@ -167,13 +170,13 @@ << expected_frame_type_ << " frame, but instead received an unknown frame of type " << header.type; - SetSpdyErrorAndNotify(SpdyError::SPDY_UNEXPECTED_FRAME); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_UNEXPECTED_FRAME); return false; } else if (!valid_stream) { // Report an invalid frame error if the stream_id is not valid. VLOG(1) << "Unknown control frame type " << header.type << " received on invalid stream " << header.stream_id; - SetSpdyErrorAndNotify(SpdyError::SPDY_INVALID_CONTROL_FRAME); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INVALID_CONTROL_FRAME); return false; } else { DVLOG(1) << "Ignoring unknown frame type " << header.type; @@ -185,21 +188,21 @@ if (!IsValidHTTP2FrameStreamId(header.stream_id, frame_type)) { VLOG(1) << "The framer received an invalid streamID of " << header.stream_id << " for a frame of type " << header.type; - SetSpdyErrorAndNotify(SpdyError::SPDY_INVALID_STREAM_ID); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INVALID_STREAM_ID); return false; } if (has_expected_frame_type_ && header.type != expected_frame_type_) { VLOG(1) << "Expected frame type " << expected_frame_type_ << ", not " << header.type; - SetSpdyErrorAndNotify(SpdyError::SPDY_UNEXPECTED_FRAME); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_UNEXPECTED_FRAME); return false; } if (!has_expected_frame_type_ && header.type == Http2FrameType::CONTINUATION) { VLOG(1) << "Got CONTINUATION frame when not expected."; - SetSpdyErrorAndNotify(SpdyError::SPDY_UNEXPECTED_FRAME); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_UNEXPECTED_FRAME); return false; } @@ -208,7 +211,7 @@ uint8_t valid_flags = Http2FrameFlag::FLAG_PADDED | Http2FrameFlag::FLAG_END_STREAM; if (header.HasAnyFlags(~valid_flags)) { - SetSpdyErrorAndNotify(SpdyError::SPDY_INVALID_DATA_FRAME_FLAGS); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INVALID_DATA_FRAME_FLAGS); return false; } } @@ -285,7 +288,7 @@ DVLOG(1) << "OnHpackFragment: len=" << len; on_hpack_fragment_called_ = true; if (!GetHpackDecoder()->HandleControlFrameHeadersData(data, len)) { - SetSpdyErrorAndNotify(SpdyError::SPDY_DECOMPRESS_FAILURE); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_DECOMPRESS_FAILURE); return; } } @@ -310,7 +313,7 @@ if (IsOkToStartFrame(header) && HasRequiredStreamId(header)) { DCHECK(has_hpack_first_frame_header_); if (header.stream_id != hpack_first_frame_header_.stream_id) { - SetSpdyErrorAndNotify(SpdyError::SPDY_UNEXPECTED_FRAME); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_UNEXPECTED_FRAME); return; } frame_header_ = header; @@ -345,16 +348,11 @@ } void OnRstStream(const Http2FrameHeader& header, - Http2ErrorCode error_code) override { - DVLOG(1) << "OnRstStream: " << header << "; code=" << error_code; + Http2ErrorCode http2_error_code) override { + DVLOG(1) << "OnRstStream: " << header << "; code=" << http2_error_code; if (IsOkToStartFrame(header) && HasRequiredStreamId(header)) { - // Treat unrecognized error codes as INTERNAL_ERROR as - // recommended by the HTTP/2 spec. - if (!IsSupportedHttp2ErrorCode(error_code)) { - error_code = Http2ErrorCode::INTERNAL_ERROR; - } SpdyRstStreamStatus status = - ParseRstStreamStatus(static_cast<int>(error_code)); + ParseRstStreamStatus(static_cast<int>(http2_error_code)); visitor()->OnRstStream(header.stream_id, status); } } @@ -398,7 +396,7 @@ << "; total_padding_length: " << total_padding_length; if (IsOkToStartFrame(header) && HasRequiredStreamId(header)) { if (promise.promised_stream_id == 0) { - SetSpdyErrorAndNotify(SpdyError::SPDY_INVALID_CONTROL_FRAME); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INVALID_CONTROL_FRAME); return; } frame_header_ = header; @@ -495,7 +493,7 @@ if (!SpdyAltSvcWireFormat::ParseHeaderFieldValue(alt_svc_value_, &altsvc_vector)) { DLOG(ERROR) << "SpdyAltSvcWireFormat::ParseHeaderFieldValue failed."; - SetSpdyErrorAndNotify(SpdyError::SPDY_INVALID_CONTROL_FRAME); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INVALID_CONTROL_FRAME); return; } visitor()->OnAltSvc(frame_header_.stream_id, alt_svc_origin_, @@ -531,33 +529,33 @@ if (header.type == Http2FrameType::DATA) { if (header.payload_length == 0) { DCHECK_EQ(1u, missing_length); - SetSpdyErrorAndNotify(SpdyError::SPDY_INVALID_DATA_FRAME_FLAGS); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INVALID_DATA_FRAME_FLAGS); return; } visitor()->OnStreamPadding(header.stream_id, 1); } - SetSpdyErrorAndNotify(SpdyError::SPDY_INVALID_PADDING); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INVALID_PADDING); } void OnFrameSizeError(const Http2FrameHeader& header) override { DVLOG(1) << "OnFrameSizeError: " << header; size_t recv_limit = outer_framer_->recv_frame_size_limit(); if (header.payload_length > recv_limit) { - SetSpdyErrorAndNotify(SpdyError::SPDY_OVERSIZED_PAYLOAD); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_OVERSIZED_PAYLOAD); return; } if (header.type != Http2FrameType::DATA && header.payload_length > recv_limit) { - SetSpdyErrorAndNotify(SpdyError::SPDY_CONTROL_PAYLOAD_TOO_LARGE); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_CONTROL_PAYLOAD_TOO_LARGE); return; } switch (header.type) { case Http2FrameType::GOAWAY: case Http2FrameType::ALTSVC: - SetSpdyErrorAndNotify(SpdyError::SPDY_INVALID_CONTROL_FRAME); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INVALID_CONTROL_FRAME); break; default: - SetSpdyErrorAndNotify(SpdyError::SPDY_INVALID_CONTROL_FRAME_SIZE); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INVALID_CONTROL_FRAME_SIZE); } } @@ -571,10 +569,10 @@ if (spdy_state_ != SpdyFramer::SPDY_ERROR) { DetermineSpdyState(status); } else { - VLOG(1) << "ProcessInputFrame spdy_error_=" - << SpdyFramer::ErrorCodeToString(spdy_error_); - if (spdy_error_ == SpdyError::SPDY_INVALID_PADDING && has_frame_header_ && - frame_type() != Http2FrameType::DATA) { + VLOG(1) << "ProcessInputFrame spdy_framer_error_=" + << SpdyFramer::SpdyFramerErrorToString(spdy_framer_error_); + if (spdy_framer_error_ == SpdyFramerError::SPDY_INVALID_PADDING && + has_frame_header_ && frame_type() != Http2FrameType::DATA) { // spdy_framer_test checks that all of the available frame payload // has been consumed, so do that. size_t total = remaining_total_payload(); @@ -596,10 +594,10 @@ // After decoding, determine the next SpdyState. Only called if the current // state is NOT SpdyState::SPDY_ERROR (i.e. if none of the callback methods // detected an error condition), because otherwise we assume that the callback - // method has set spdy_error_ appropriately. + // method has set spdy_framer_error_ appropriately. void DetermineSpdyState(DecodeStatus status) { - DCHECK_EQ(spdy_error_, SpdyFramer::SPDY_NO_ERROR); - DCHECK(!HasError()) << spdy_error_; + DCHECK_EQ(spdy_framer_error_, SpdyFramer::SPDY_NO_ERROR); + DCHECK(!HasError()) << spdy_framer_error_; switch (status) { case DecodeStatus::kDecodeDone: DVLOG(1) << "ProcessInputFrame -> DecodeStatus::kDecodeDone"; @@ -638,9 +636,10 @@ SPDY_BUG << "Expected to be done decoding the frame, not " << status; SetSpdyErrorAndNotify(SpdyFramer::SPDY_INTERNAL_FRAMER_ERROR); - } else if (spdy_error_ != SpdyFramer::SPDY_NO_ERROR) { + } else if (spdy_framer_error_ != SpdyFramer::SPDY_NO_ERROR) { SPDY_BUG << "Expected to have no error, not " - << SpdyFramer::ErrorCodeToString(spdy_error_); + << SpdyFramer::SpdyFramerErrorToString( + spdy_framer_error_); } else { ResetBetweenFrames(); } @@ -665,7 +664,7 @@ // otherwise (i.e. not between every frame). void ResetInternal() { set_spdy_state(SpdyState::SPDY_READY_FOR_FRAME); - spdy_error_ = SpdyError::SPDY_NO_ERROR; + spdy_framer_error_ = SpdyFramerError::SPDY_NO_ERROR; decoded_frame_header_ = false; has_frame_header_ = false; @@ -685,14 +684,14 @@ spdy_state_ = v; } - void SetSpdyErrorAndNotify(SpdyError error) { + void SetSpdyErrorAndNotify(SpdyFramerError error) { if (HasError()) { DCHECK_EQ(spdy_state_, SpdyState::SPDY_ERROR); } else { VLOG(2) << "SetSpdyErrorAndNotify(" - << SpdyFramer::ErrorCodeToString(error) << ")"; - DCHECK_NE(error, SpdyError::SPDY_NO_ERROR); - spdy_error_ = error; + << SpdyFramer::SpdyFramerErrorToString(error) << ")"; + DCHECK_NE(error, SpdyFramerError::SPDY_NO_ERROR); + spdy_framer_error_ = error; set_spdy_state(SpdyState::SPDY_ERROR); frame_decoder_->set_listener(&no_op_listener_); visitor()->OnError(outer_framer_); @@ -701,10 +700,10 @@ bool HasError() const { if (spdy_state_ == SpdyState::SPDY_ERROR) { - DCHECK_NE(error_code(), SpdyError::SPDY_NO_ERROR); + DCHECK_NE(spdy_framer_error(), SpdyFramerError::SPDY_NO_ERROR); return true; } else { - DCHECK_EQ(error_code(), SpdyError::SPDY_NO_ERROR); + DCHECK_EQ(spdy_framer_error(), SpdyFramerError::SPDY_NO_ERROR); return false; } } @@ -757,7 +756,7 @@ if (has_expected_frame_type_ && header.type != expected_frame_type_) { VLOG(1) << "Expected frame type " << expected_frame_type_ << ", not " << header.type; - SetSpdyErrorAndNotify(SpdyError::SPDY_UNEXPECTED_FRAME); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_UNEXPECTED_FRAME); return false; } @@ -774,7 +773,7 @@ return true; } VLOG(1) << "Stream Id is required, but zero provided"; - SetSpdyErrorAndNotify(SpdyError::SPDY_INVALID_STREAM_ID); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INVALID_STREAM_ID); return false; } @@ -792,7 +791,7 @@ return true; } VLOG(1) << "Stream Id was not zero, as required: " << stream_id; - SetSpdyErrorAndNotify(SpdyError::SPDY_INVALID_STREAM_ID); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INVALID_STREAM_ID); return false; } @@ -829,7 +828,7 @@ visitor()->OnHeaderFrameStart(stream_id()); if (handler == nullptr) { SPDY_BUG << "visitor_->OnHeaderFrameStart returned nullptr"; - SetSpdyErrorAndNotify(SpdyError::SPDY_INTERNAL_FRAMER_ERROR); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INTERNAL_FRAMER_ERROR); return; } GetHpackDecoder()->HandleControlFrameHeadersStart(handler); @@ -860,7 +859,7 @@ if (GetHpackDecoder()->HandleControlFrameHeadersComplete(nullptr)) { visitor()->OnHeaderFrameEnd(stream_id(), true); } else { - SetSpdyErrorAndNotify(SpdyError::SPDY_DECOMPRESS_FAILURE); + SetSpdyErrorAndNotify(SpdyFramerError::SPDY_DECOMPRESS_FAILURE); return; } const Http2FrameHeader& first = @@ -918,10 +917,10 @@ // SETTINGS frame as the first frame. Http2FrameType expected_frame_type_; - // Attempt to duplicate the SpdyState and SpdyError values that SpdyFramer - // sets. Values determined by getting tests to pass. + // Attempt to duplicate the SpdyState and SpdyFramerError values that + // SpdyFramer sets. Values determined by getting tests to pass. SpdyState spdy_state_; - SpdyError spdy_error_; + SpdyFramerError spdy_framer_error_; // Has OnFrameHeader been called? bool decoded_frame_header_ = false;
diff --git a/net/spdy/mock_spdy_framer_visitor.h b/net/spdy/mock_spdy_framer_visitor.h index 4f48ed0..ca73dfc 100644 --- a/net/spdy/mock_spdy_framer_visitor.h +++ b/net/spdy/mock_spdy_framer_visitor.h
@@ -8,6 +8,7 @@ #include <stddef.h> #include <stdint.h> +#include <cstdint> #include <memory> #include "base/strings/string_piece.h" @@ -68,7 +69,8 @@ SpdyStreamId parent_stream_id, int weight, bool exclusive)); - MOCK_METHOD2(OnUnknownFrame, bool(SpdyStreamId stream_id, int frame_type)); + MOCK_METHOD2(OnUnknownFrame, + bool(SpdyStreamId stream_id, uint8_t frame_type)); void DelegateHeaderHandling() { ON_CALL(*this, OnHeaderFrameStart(testing::_))
diff --git a/net/spdy/spdy_deframer_visitor.cc b/net/spdy/spdy_deframer_visitor.cc index fd0ef33..71deab4 100644 --- a/net/spdy/spdy_deframer_visitor.cc +++ b/net/spdy/spdy_deframer_visitor.cc
@@ -8,6 +8,7 @@ #include <string.h> #include <algorithm> +#include <cstdint> #include <limits> #include "base/logging.h" @@ -178,7 +179,7 @@ size_t len) override; void OnStreamEnd(SpdyStreamId stream_id) override; void OnStreamPadding(SpdyStreamId stream_id, size_t len) override; - bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override; + bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override; void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) override; // Callbacks defined in SpdyHeadersHandlerInterface. @@ -481,7 +482,7 @@ // The SpdyFramer will not process any more data at this point. void SpdyTestDeframerImpl::OnError(SpdyFramer* framer) { DVLOG(1) << "SpdyFramer detected an error in the stream: " - << SpdyFramer::ErrorCodeToString(framer->error_code()) + << SpdyFramer::SpdyFramerErrorToString(framer->spdy_framer_error()) << " frame_type_: " << Http2FrameTypeToString(frame_type_); listener_->OnError(framer, this); } @@ -746,7 +747,7 @@ // of the set of currently open streams. For now we'll assume that unknown // frame types are unsupported. bool SpdyTestDeframerImpl::OnUnknownFrame(SpdyStreamId stream_id, - int frame_type) { + uint8_t frame_type) { DVLOG(1) << "OnAltSvc stream_id: " << stream_id; CHECK_EQ(frame_type_, UNSET) << " frame_type_=" << Http2FrameTypeToString(frame_type_);
diff --git a/net/spdy/spdy_deframer_visitor_test.cc b/net/spdy/spdy_deframer_visitor_test.cc index cb946a1..be09409 100644 --- a/net/spdy/spdy_deframer_visitor_test.cc +++ b/net/spdy/spdy_deframer_visitor_test.cc
@@ -45,7 +45,7 @@ bool DeframeInput(const char* input, size_t size) { size_t input_remaining = size; while (input_remaining > 0 && - decoder_.error_code() == SpdyFramer::SPDY_NO_ERROR) { + decoder_.spdy_framer_error() == SpdyFramer::SPDY_NO_ERROR) { // To make the tests more interesting, we feed random (and small) chunks // into the framer. This simulates getting strange-sized reads from // the socket. @@ -60,7 +60,7 @@ } } return (input_remaining == 0 && - decoder_.error_code() == SpdyFramer::SPDY_NO_ERROR); + decoder_.spdy_framer_error() == SpdyFramer::SPDY_NO_ERROR); } SpdySerializedFrame SerializeFrame(const SpdyFrameIR& frame) {
diff --git a/net/spdy/spdy_frame_builder.cc b/net/spdy/spdy_frame_builder.cc index 141b15a..a92aaecb 100644 --- a/net/spdy/spdy_frame_builder.cc +++ b/net/spdy/spdy_frame_builder.cc
@@ -39,7 +39,7 @@ SpdyFrameType type, uint8_t flags, SpdyStreamId stream_id) { - DCHECK(IsDefinedFrameType(SerializeFrameType(type))); + DCHECK(IsDefinedFrameType(type)); DCHECK_EQ(0u, stream_id & ~kStreamIdMask); bool success = true; if (length_ > 0) { @@ -58,7 +58,7 @@ // Don't check for length limits here because this may be larger than the // actual frame length. success &= WriteUInt24(capacity_ - offset_ - kFrameHeaderSize); - success &= WriteUInt8(SerializeFrameType(type)); + success &= WriteUInt8(type); success &= WriteUInt8(flags); success &= WriteUInt32(stream_id); DCHECK_EQ(framer.GetDataFrameMinimumSize(), length_); @@ -70,7 +70,7 @@ uint8_t flags, SpdyStreamId stream_id, size_t length) { - DCHECK(IsDefinedFrameType(SerializeFrameType(type))); + DCHECK(IsDefinedFrameType(type)); DCHECK_EQ(0u, stream_id & ~kStreamIdMask); bool success = true; SPDY_BUG_IF(framer.GetFrameMaximumSize() < length_) @@ -81,7 +81,7 @@ length_ = 0; success &= WriteUInt24(length); - success &= WriteUInt8(SerializeFrameType(type)); + success &= WriteUInt8(type); success &= WriteUInt8(flags); success &= WriteUInt32(stream_id); DCHECK_EQ(framer.GetDataFrameMinimumSize(), length_);
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index 7e0d450..b4abb09 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc
@@ -174,7 +174,7 @@ } state_ = SPDY_READY_FOR_FRAME; previous_state_ = SPDY_READY_FOR_FRAME; - error_code_ = SPDY_NO_ERROR; + spdy_framer_error_ = SPDY_NO_ERROR; remaining_data_length_ = 0; remaining_control_header_ = 0; current_frame_buffer_.Rewind(); @@ -216,11 +216,11 @@ return probable_http_response_; } -SpdyFramer::SpdyError SpdyFramer::error_code() const { +SpdyFramer::SpdyFramerError SpdyFramer::spdy_framer_error() const { if (decoder_adapter_ != nullptr) { - return decoder_adapter_->error_code(); + return decoder_adapter_->spdy_framer_error(); } - return error_code_; + return spdy_framer_error_; } SpdyFramer::SpdyState SpdyFramer::state() const { @@ -360,9 +360,9 @@ return "UNKNOWN_STATE"; } -void SpdyFramer::set_error(SpdyError error) { +void SpdyFramer::set_error(SpdyFramerError error) { DCHECK(visitor_); - error_code_ = error; + spdy_framer_error_ = error; // These values will usually get reset once we come to the end // of a header block, but if we run into an error that // might not happen, so reset them here. @@ -373,8 +373,8 @@ visitor_->OnError(this); } -const char* SpdyFramer::ErrorCodeToString(int error_code) { - switch (error_code) { +const char* SpdyFramer::SpdyFramerErrorToString(int spdy_framer_error) { + switch (spdy_framer_error) { case SPDY_NO_ERROR: return "NO_ERROR"; case SPDY_INVALID_STREAM_ID: @@ -791,7 +791,7 @@ } void SpdyFramer::ProcessControlFrameHeader() { - DCHECK_EQ(SPDY_NO_ERROR, error_code_); + DCHECK_EQ(SPDY_NO_ERROR, spdy_framer_error_); DCHECK_LE(GetFrameHeaderSize(), current_frame_buffer_.len()); // Do some sanity checking on the control frame sizes and flags. @@ -1804,7 +1804,7 @@ builder.BeginNewFrame(*this, RST_STREAM, 0, rst_stream.stream_id()); - builder.WriteUInt32(SerializeRstStreamStatus(rst_stream.status())); + builder.WriteUInt32(rst_stream.status()); DCHECK_EQ(expected_length, builder.length()); return builder.take(); @@ -1869,7 +1869,7 @@ builder.WriteUInt32(goaway.last_good_stream_id()); // GOAWAY frames also specify the error status code. - builder.WriteUInt32(SerializeGoAwayStatus(goaway.status())); + builder.WriteUInt32(goaway.status()); // GOAWAY frames may also specify opaque data. if (!goaway.description().empty()) {
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h index d615afcd..ca0521d 100644 --- a/net/spdy/spdy_framer.h +++ b/net/spdy/spdy_framer.h
@@ -8,6 +8,7 @@ #include <stddef.h> #include <stdint.h> +#include <cstdint> #include <map> #include <memory> #include <string> @@ -218,7 +219,7 @@ // Return true if this appears to be a valid extension frame, false otherwise. // We distinguish between extension frames and nonsense by checking // whether the stream id is valid. - virtual bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) = 0; + virtual bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) = 0; }; // Optionally, and in addition to SpdyFramerVisitorInterface, a class supporting @@ -269,8 +270,8 @@ SPDY_ALTSVC_FRAME_PAYLOAD, }; - // SPDY error codes. - enum SpdyError { + // Framer error codes. + enum SpdyFramerError { SPDY_NO_ERROR, SPDY_INVALID_STREAM_ID, // Stream ID is invalid SPDY_INVALID_CONTROL_FRAME, // Control frame is mal-formatted. @@ -356,7 +357,7 @@ void Reset(); // Check the state of the framer. - SpdyError error_code() const; + SpdyFramerError spdy_framer_error() const; SpdyState state() const; bool HasError() const { return state() == SPDY_ERROR; } @@ -491,7 +492,7 @@ // For debugging. static const char* StateToString(int state); - static const char* ErrorCodeToString(int error_code); + static const char* SpdyFramerErrorToString(int spdy_framer_error); static const char* StatusCodeToString(int status_code); static const char* FrameTypeToString(SpdyFrameType type); @@ -611,7 +612,7 @@ // // For valid frames, returns the correct SpdyFrameType. // Otherwise returns a best guess at invalid frame type, - // after setting the appropriate SpdyError. + // after setting the appropriate SpdyFramerError. SpdyFrameType ValidateFrameHeader(bool is_control_frame, int frame_type_field, size_t payload_length_field); @@ -656,7 +657,7 @@ uint8_t SerializeHeaderFrameFlags(const SpdyHeadersIR& header_ir) const; // Set the error code and moves the framer into the error state. - void set_error(SpdyError error); + void set_error(SpdyFramerError error); // The size of the control frame buffer. // Since this is only used for control frame headers, the maximum control @@ -675,7 +676,7 @@ SpdyState state_; SpdyState previous_state_; - SpdyError error_code_; + SpdyFramerError spdy_framer_error_; // Note that for DATA frame, remaining_data_length_ is sum of lengths of // frame header, padding length field (optional), data payload (optional) and
diff --git a/net/spdy/spdy_framer_decoder_adapter.cc b/net/spdy/spdy_framer_decoder_adapter.cc index 3e7a7fe..b603d0b 100644 --- a/net/spdy/spdy_framer_decoder_adapter.cc +++ b/net/spdy/spdy_framer_decoder_adapter.cc
@@ -173,13 +173,13 @@ } bool SpdyFramerVisitorAdapter::OnUnknownFrame(SpdyStreamId stream_id, - int frame_type) { + uint8_t frame_type) { return visitor_->OnUnknownFrame(stream_id, frame_type); } class NestedSpdyFramerDecoder : public SpdyFramerDecoderAdapter { typedef SpdyFramer::SpdyState SpdyState; - typedef SpdyFramer::SpdyError SpdyError; + typedef SpdyFramer::SpdyFramerError SpdyFramerError; public: explicit NestedSpdyFramerDecoder(SpdyFramer* outer) @@ -229,8 +229,8 @@ void Reset() override { framer_.Reset(); } - SpdyFramer::SpdyError error_code() const override { - return framer_.error_code(); + SpdyFramer::SpdyFramerError spdy_framer_error() const override { + return framer_.spdy_framer_error(); } SpdyFramer::SpdyState state() const override { return framer_.state(); } bool probable_http_response() const override {
diff --git a/net/spdy/spdy_framer_decoder_adapter.h b/net/spdy/spdy_framer_decoder_adapter.h index 59a33d09a..b18dd9c51 100644 --- a/net/spdy/spdy_framer_decoder_adapter.h +++ b/net/spdy/spdy_framer_decoder_adapter.h
@@ -7,6 +7,7 @@ #include <stddef.h> +#include <cstdint> #include <memory> #include "base/strings/string_piece.h" @@ -67,7 +68,7 @@ virtual SpdyFramer::SpdyState state() const = 0; // Current error code (NO_ERROR if state != ERROR). - virtual SpdyFramer::SpdyError error_code() const = 0; + virtual SpdyFramer::SpdyFramerError spdy_framer_error() const = 0; // Has any frame header looked like the start of an HTTP/1.1 (or earlier) // response? Used to detect if a backend/server that we sent a request to @@ -146,7 +147,7 @@ base::StringPiece origin, const SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector) override; - bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override; + bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override; protected: SpdyFramerVisitorInterface* visitor() const { return visitor_; }
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc index f3d862fd..a4eb0ab 100644 --- a/net/spdy/spdy_framer_test.cc +++ b/net/spdy/spdy_framer_test.cc
@@ -8,6 +8,7 @@ #include <string.h> #include <algorithm> +#include <cstdint> #include <limits> #include <memory> #include <string> @@ -171,7 +172,7 @@ // Do nothing. } - bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override { + bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override { LOG(FATAL); return false; } @@ -220,7 +221,7 @@ size_t size) { return framer->GetNumberRequiredContinuationFrames(size); } - static void SetError(SpdyFramer* framer, SpdyFramer::SpdyError error) { + static void SetError(SpdyFramer* framer, SpdyFramer::SpdyFramerError error) { framer->set_error(error); } @@ -306,7 +307,7 @@ void OnError(SpdyFramer* f) override { VLOG(1) << "SpdyFramer Error: " - << SpdyFramer::ErrorCodeToString(f->error_code()); + << SpdyFramer::SpdyFramerErrorToString(f->spdy_framer_error()); ++error_count_; } @@ -465,7 +466,7 @@ ++priority_count_; } - bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override { + bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override { VLOG(1) << "OnUnknownFrame(" << stream_id << ", " << frame_type << ")"; return on_unknown_frame_result_; } @@ -494,7 +495,7 @@ size_t input_remaining = size; const char* input_ptr = reinterpret_cast<const char*>(input); while (input_remaining > 0 && - framer_.error_code() == SpdyFramer::SPDY_NO_ERROR) { + framer_.spdy_framer_error() == SpdyFramer::SPDY_NO_ERROR) { // To make the tests more interesting, we feed random (and small) chunks // into the framer. This simulates getting strange-sized reads from // the socket. @@ -509,7 +510,7 @@ void InitHeaderStreaming(SpdyFrameType header_control_type, SpdyStreamId stream_id) { - if (!IsDefinedFrameType(SerializeFrameType(header_control_type))) { + if (!IsDefinedFrameType(header_control_type)) { DLOG(FATAL) << "Attempted to init header streaming with " << "invalid control frame type: " << header_control_type; } @@ -819,8 +820,8 @@ EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); framer.ProcessInput(frame.data(), frame.size()); EXPECT_TRUE(framer.HasError()); - EXPECT_EQ(SpdyFramer::SPDY_OVERSIZED_PAYLOAD, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_OVERSIZED_PAYLOAD, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } // Test that if we receive a DATA frame with padding length larger than the @@ -854,8 +855,8 @@ } EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); EXPECT_TRUE(framer.HasError()); - EXPECT_EQ(SpdyFramer::SPDY_INVALID_PADDING, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_PADDING, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } // Test that if we receive a DATA frame with padding length not larger than the @@ -889,8 +890,8 @@ EXPECT_EQ(frame.size(), framer.ProcessInput(frame.data(), frame.size())); EXPECT_FALSE(framer.HasError()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } // Test that if we receive a HEADERS frame with padding length larger than the @@ -921,8 +922,8 @@ EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); EXPECT_EQ(frame.size(), framer.ProcessInput(frame.data(), frame.size())); EXPECT_TRUE(framer.HasError()); - EXPECT_EQ(SpdyFramer::SPDY_INVALID_PADDING, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_PADDING, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } // Test that if we receive a HEADERS frame with padding length not larger @@ -948,8 +949,8 @@ EXPECT_CALL(visitor, OnHeaderFrameStart(1)).Times(1); EXPECT_EQ(frame.size(), framer.ProcessInput(frame.data(), frame.size())); EXPECT_FALSE(framer.HasError()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } // Test that if we receive a DATA with stream ID zero, we signal an error @@ -967,8 +968,8 @@ EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); EXPECT_TRUE(framer.HasError()); - EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } // Test that if we receive a HEADERS with stream ID zero, we signal an error @@ -986,8 +987,8 @@ EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); EXPECT_TRUE(framer.HasError()); - EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } // Test that if we receive a PRIORITY with stream ID zero, we signal an error @@ -1004,8 +1005,8 @@ EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); EXPECT_TRUE(framer.HasError()); - EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } // Test that if we receive a RST_STREAM with stream ID zero, we signal an error @@ -1022,8 +1023,8 @@ EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); EXPECT_TRUE(framer.HasError()); - EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } // Test that if we receive a SETTINGS with stream ID other than zero, @@ -1049,8 +1050,8 @@ EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); EXPECT_TRUE(framer.HasError()); - EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } // Test that if we receive a GOAWAY with stream ID other than zero, @@ -1077,8 +1078,8 @@ EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); EXPECT_TRUE(framer.HasError()); - EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } // Test that if we receive a CONTINUATION with stream ID zero, we signal an @@ -1099,8 +1100,8 @@ EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); EXPECT_TRUE(framer.HasError()); - EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } // Test that if we receive a PUSH_PROMISE with stream ID zero, we signal an @@ -1118,8 +1119,8 @@ EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); EXPECT_TRUE(framer.HasError()); - EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } // Test that if we receive a PUSH_PROMISE with promised stream ID zero, we @@ -1136,8 +1137,8 @@ EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); framer.ProcessInput(frame.data(), frame.size()); EXPECT_TRUE(framer.HasError()); - EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } TEST_P(SpdyFramerTest, DuplicateHeader) { @@ -2134,7 +2135,7 @@ SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); const char kDescription[] = "BLOCKED frame"; - const char kType = static_cast<unsigned char>(SerializeFrameType(BLOCKED)); + const unsigned char kType = static_cast<unsigned char>(BLOCKED); const unsigned char kFrameData[] = { 0x00, 0x00, 0x00, // Length: 0 kType, // Type: BLOCKED @@ -2376,8 +2377,8 @@ EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); EXPECT_TRUE(framer.HasError()); - EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } TEST_P(SpdyFramerTest, CreatePushPromiseThenContinuationUncompressed) { @@ -2484,7 +2485,7 @@ SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); const char kDescription[] = "ALTSVC frame"; - const char kType = static_cast<unsigned char>(SerializeFrameType(ALTSVC)); + const char kType = static_cast<unsigned char>(ALTSVC); const unsigned char kFrameData[] = { 0x00, 0x00, 0x49, kType, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 'o', 'r', 'i', 'g', 'i', 'n', 'p', 'i', 'd', '1', '=', '"', 'h', @@ -2734,8 +2735,9 @@ EXPECT_EQ(1, visitor.error_count_); // This generated an error. EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, - visitor.framer_.error_code()) - << SpdyFramer::ErrorCodeToString(visitor.framer_.error_code()); + visitor.framer_.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString( + visitor.framer_.spdy_framer_error()); EXPECT_EQ(0, visitor.goaway_count_); // Frame not parsed. } @@ -2774,8 +2776,9 @@ // settings frame of length kNewLength. EXPECT_EQ(1, visitor.error_count_); EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE, - visitor.framer_.error_code()) - << SpdyFramer::ErrorCodeToString(visitor.framer_.error_code()); + visitor.framer_.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString( + visitor.framer_.spdy_framer_error()); } // Tests handling of SETTINGS frames larger than the frame buffer size. @@ -2924,42 +2927,42 @@ CHECK_EQ(framer.GetDataFrameMinimumSize(), framer.ProcessInput(frame.data(), framer.GetDataFrameMinimumSize())); CHECK_EQ(framer.state(), SpdyFramer::SPDY_READ_DATA_FRAME_PADDING_LENGTH); - CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR); + CHECK_EQ(framer.spdy_framer_error(), SpdyFramer::SPDY_NO_ERROR); bytes_consumed += framer.GetDataFrameMinimumSize(); // Send the padding length field. EXPECT_CALL(visitor, OnStreamPadding(1, 1)); CHECK_EQ(1u, framer.ProcessInput(frame.data() + bytes_consumed, 1)); CHECK_EQ(framer.state(), SpdyFramer::SPDY_FORWARD_STREAM_FRAME); - CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR); + CHECK_EQ(framer.spdy_framer_error(), SpdyFramer::SPDY_NO_ERROR); bytes_consumed += 1; // Send the first two bytes of the data payload, i.e., "he". EXPECT_CALL(visitor, OnStreamFrameData(1, _, 2)); CHECK_EQ(2u, framer.ProcessInput(frame.data() + bytes_consumed, 2)); CHECK_EQ(framer.state(), SpdyFramer::SPDY_FORWARD_STREAM_FRAME); - CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR); + CHECK_EQ(framer.spdy_framer_error(), SpdyFramer::SPDY_NO_ERROR); bytes_consumed += 2; // Send the rest three bytes of the data payload, i.e., "llo". EXPECT_CALL(visitor, OnStreamFrameData(1, _, 3)); CHECK_EQ(3u, framer.ProcessInput(frame.data() + bytes_consumed, 3)); CHECK_EQ(framer.state(), SpdyFramer::SPDY_CONSUME_PADDING); - CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR); + CHECK_EQ(framer.spdy_framer_error(), SpdyFramer::SPDY_NO_ERROR); bytes_consumed += 3; // Send the first 100 bytes of the padding payload. EXPECT_CALL(visitor, OnStreamPadding(1, 100)); CHECK_EQ(100u, framer.ProcessInput(frame.data() + bytes_consumed, 100)); CHECK_EQ(framer.state(), SpdyFramer::SPDY_CONSUME_PADDING); - CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR); + CHECK_EQ(framer.spdy_framer_error(), SpdyFramer::SPDY_NO_ERROR); bytes_consumed += 100; // Send rest of the padding payload. EXPECT_CALL(visitor, OnStreamPadding(1, 18)); CHECK_EQ(18u, framer.ProcessInput(frame.data() + bytes_consumed, 18)); CHECK_EQ(framer.state(), SpdyFramer::SPDY_READY_FOR_FRAME); - CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR); + CHECK_EQ(framer.spdy_framer_error(), SpdyFramer::SPDY_NO_ERROR); } TEST_P(SpdyFramerTest, ReadWindowUpdate) { @@ -3182,8 +3185,10 @@ visitor.SimulateInFramer(kInput, sizeof(kInput)); EXPECT_EQ(1, visitor.error_count_); - EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, visitor.framer_.error_code()) - << SpdyFramer::ErrorCodeToString(visitor.framer_.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, + visitor.framer_.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString( + visitor.framer_.spdy_framer_error()); EXPECT_EQ(1, visitor.headers_frame_count_); EXPECT_EQ(0, visitor.continuation_count_); EXPECT_EQ(0u, visitor.header_buffer_length_); @@ -3217,8 +3222,10 @@ visitor.SimulateInFramer(kInput, sizeof(kInput)); EXPECT_EQ(1, visitor.error_count_); - EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, visitor.framer_.error_code()) - << SpdyFramer::ErrorCodeToString(visitor.framer_.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, + visitor.framer_.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString( + visitor.framer_.spdy_framer_error()); EXPECT_EQ(1, visitor.headers_frame_count_); EXPECT_EQ(0, visitor.continuation_count_); EXPECT_EQ(0u, visitor.header_buffer_length_); @@ -3242,8 +3249,10 @@ visitor.SimulateInFramer(kInput, sizeof(kInput)); EXPECT_EQ(1, visitor.error_count_); - EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, visitor.framer_.error_code()) - << SpdyFramer::ErrorCodeToString(visitor.framer_.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, + visitor.framer_.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString( + visitor.framer_.spdy_framer_error()); EXPECT_EQ(0, visitor.continuation_count_); EXPECT_EQ(0u, visitor.header_buffer_length_); } @@ -3273,8 +3282,10 @@ visitor.SimulateInFramer(kInput, sizeof(kInput)); EXPECT_EQ(1, visitor.error_count_); - EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, visitor.framer_.error_code()) - << SpdyFramer::ErrorCodeToString(visitor.framer_.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, + visitor.framer_.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString( + visitor.framer_.spdy_framer_error()); EXPECT_EQ(1, visitor.headers_frame_count_); EXPECT_EQ(0, visitor.continuation_count_); EXPECT_EQ(0u, visitor.header_buffer_length_); @@ -3308,8 +3319,10 @@ visitor.SimulateInFramer(kInput, sizeof(kInput)); EXPECT_EQ(1, visitor.error_count_); - EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, visitor.framer_.error_code()) - << SpdyFramer::ErrorCodeToString(visitor.framer_.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, + visitor.framer_.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString( + visitor.framer_.spdy_framer_error()); EXPECT_EQ(1, visitor.headers_frame_count_); EXPECT_EQ(0, visitor.continuation_count_); EXPECT_EQ(0u, visitor.header_buffer_length_); @@ -3445,37 +3458,37 @@ TEST_P(SpdyFramerTest, ErrorCodeToStringTest) { EXPECT_STREQ("NO_ERROR", - SpdyFramer::ErrorCodeToString(SpdyFramer::SPDY_NO_ERROR)); - EXPECT_STREQ("INVALID_STREAM_ID", SpdyFramer::ErrorCodeToString( + SpdyFramer::SpdyFramerErrorToString(SpdyFramer::SPDY_NO_ERROR)); + EXPECT_STREQ("INVALID_STREAM_ID", SpdyFramer::SpdyFramerErrorToString( SpdyFramer::SPDY_INVALID_STREAM_ID)); - EXPECT_STREQ( - "INVALID_CONTROL_FRAME", - SpdyFramer::ErrorCodeToString(SpdyFramer::SPDY_INVALID_CONTROL_FRAME)); + EXPECT_STREQ("INVALID_CONTROL_FRAME", + SpdyFramer::SpdyFramerErrorToString( + SpdyFramer::SPDY_INVALID_CONTROL_FRAME)); EXPECT_STREQ("CONTROL_PAYLOAD_TOO_LARGE", - SpdyFramer::ErrorCodeToString( + SpdyFramer::SpdyFramerErrorToString( SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE)); EXPECT_STREQ("INVALID_CONTROL_FRAME_SIZE", - SpdyFramer::ErrorCodeToString( + SpdyFramer::SpdyFramerErrorToString( SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE)); - EXPECT_STREQ("ZLIB_INIT_FAILURE", SpdyFramer::ErrorCodeToString( + EXPECT_STREQ("ZLIB_INIT_FAILURE", SpdyFramer::SpdyFramerErrorToString( SpdyFramer::SPDY_ZLIB_INIT_FAILURE)); - EXPECT_STREQ( - "UNSUPPORTED_VERSION", - SpdyFramer::ErrorCodeToString(SpdyFramer::SPDY_UNSUPPORTED_VERSION)); - EXPECT_STREQ("DECOMPRESS_FAILURE", SpdyFramer::ErrorCodeToString( + EXPECT_STREQ("UNSUPPORTED_VERSION", + SpdyFramer::SpdyFramerErrorToString( + SpdyFramer::SPDY_UNSUPPORTED_VERSION)); + EXPECT_STREQ("DECOMPRESS_FAILURE", SpdyFramer::SpdyFramerErrorToString( SpdyFramer::SPDY_DECOMPRESS_FAILURE)); - EXPECT_STREQ("COMPRESS_FAILURE", SpdyFramer::ErrorCodeToString( + EXPECT_STREQ("COMPRESS_FAILURE", SpdyFramer::SpdyFramerErrorToString( SpdyFramer::SPDY_COMPRESS_FAILURE)); - EXPECT_STREQ("SPDY_INVALID_PADDING", - SpdyFramer::ErrorCodeToString(SpdyFramer::SPDY_INVALID_PADDING)); - EXPECT_STREQ( - "SPDY_INVALID_DATA_FRAME_FLAGS", - SpdyFramer::ErrorCodeToString(SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS)); + EXPECT_STREQ("SPDY_INVALID_PADDING", SpdyFramer::SpdyFramerErrorToString( + SpdyFramer::SPDY_INVALID_PADDING)); + EXPECT_STREQ("SPDY_INVALID_DATA_FRAME_FLAGS", + SpdyFramer::SpdyFramerErrorToString( + SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS)); EXPECT_STREQ("SPDY_INVALID_CONTROL_FRAME_FLAGS", - SpdyFramer::ErrorCodeToString( + SpdyFramer::SpdyFramerErrorToString( SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS)); EXPECT_STREQ("UNKNOWN_ERROR", - SpdyFramer::ErrorCodeToString(SpdyFramer::LAST_ERROR)); + SpdyFramer::SpdyFramerErrorToString(SpdyFramer::LAST_ERROR)); } TEST_P(SpdyFramerTest, StatusCodeToStringTest) { @@ -3556,16 +3569,17 @@ framer.ProcessInput(frame.data(), frame.size()); if (flags & ~valid_data_flags) { EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS, + framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } else if (flags & DATA_FLAG_PADDED) { EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_INVALID_PADDING, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_PADDING, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } else { EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } } while (++flags != 0); } @@ -3589,8 +3603,8 @@ framer.ProcessInput(frame.data(), frame.size()); EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } while (++flags != 0); } @@ -3622,12 +3636,12 @@ // The frame is invalid because ACK frames should have no payload. EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state()); EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE, - framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } else { EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } } while (++flags != 0); } @@ -3650,8 +3664,8 @@ framer.ProcessInput(frame.data(), frame.size()); EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } while (++flags != 0); } @@ -3707,8 +3721,8 @@ framer.ProcessInput(frame.data(), frame.size()); EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } while (++flags != 0); } @@ -3733,8 +3747,8 @@ framer.ProcessInput(frame.data(), frame.size()); EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } while (++flags != 0); } @@ -3756,8 +3770,8 @@ framer.ProcessInput(frame.data(), frame.size()); EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } while (++flags != 0); } @@ -3796,8 +3810,8 @@ framer.ProcessInput(frame.data(), frame.size()); EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } while (++flags != 0); } @@ -3838,8 +3852,8 @@ framer.ProcessInput(frame0.data(), frame0.size()); framer.ProcessInput(frame.data(), frame.size()); EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } while (++flags != 0); } @@ -3872,8 +3886,8 @@ framer.ProcessInput(reinterpret_cast<const char*>(kH2RstStreamInvalid), arraysize(kH2RstStreamInvalid)); EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); framer.Reset(); @@ -3881,8 +3895,8 @@ framer.ProcessInput(reinterpret_cast<const char*>(kH2RstStreamNumStatusCodes), arraysize(kH2RstStreamNumStatusCodes)); EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } // Test handling of GOAWAY frames with out-of-bounds status code. @@ -3904,8 +3918,8 @@ framer.ProcessInput(reinterpret_cast<const char*>(kH2FrameData), arraysize(kH2FrameData)); EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } // Tests handling of a GOAWAY frame with out-of-bounds stream ID. @@ -3927,8 +3941,8 @@ framer.ProcessInput(reinterpret_cast<const char*>(kH2FrameData), arraysize(kH2FrameData)); EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } TEST_P(SpdyFramerTest, OnBlocked) { @@ -3945,8 +3959,8 @@ framer.ProcessInput(frame.data(), framer.GetBlockedSize()); EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } TEST_P(SpdyFramerTest, OnAltSvc) { @@ -3974,8 +3988,8 @@ framer.ProcessInput(frame.data(), frame.size()); EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } TEST_P(SpdyFramerTest, OnAltSvcNoOrigin) { @@ -4001,8 +4015,8 @@ framer.ProcessInput(frame.data(), frame.size()); EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } TEST_P(SpdyFramerTest, OnAltSvcEmptyProtocolId) { @@ -4022,12 +4036,12 @@ framer.ProcessInput(frame.data(), frame.size()); EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); } TEST_P(SpdyFramerTest, OnAltSvcBadLengths) { - const char kType = static_cast<unsigned char>(SerializeFrameType(ALTSVC)); + const char kType = static_cast<unsigned char>(ALTSVC); const unsigned char kFrameDataOriginLenLargerThanFrame[] = { 0x00, 0x00, 0x05, kType, 0x00, 0x00, 0x00, 0x00, 0x03, 0x42, 0x42, 'f', 'o', 'o', @@ -4041,7 +4055,7 @@ EXPECT_EQ(1, visitor.error_count_); EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, - visitor.framer_.error_code()); + visitor.framer_.spdy_framer_error()); } // Tests handling of ALTSVC frames delivered in small chunks. @@ -4088,8 +4102,8 @@ framer.ProcessInput(frame.data(), frame.size()); EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); // TODO(mlavan): once we actually maintain a priority tree, // check that state is adjusted correctly. } @@ -4110,8 +4124,9 @@ EXPECT_EQ(SpdyFramer::SPDY_ERROR, visitor.framer_.state()); EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE, - visitor.framer_.error_code()) - << SpdyFramer::ErrorCodeToString(visitor.framer_.error_code()); + visitor.framer_.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString( + visitor.framer_.spdy_framer_error()); } // Tests handling of PING frame with incorrect size. @@ -4130,8 +4145,9 @@ EXPECT_EQ(SpdyFramer::SPDY_ERROR, visitor.framer_.state()); EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE, - visitor.framer_.error_code()) - << SpdyFramer::ErrorCodeToString(visitor.framer_.error_code()); + visitor.framer_.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString( + visitor.framer_.spdy_framer_error()); } // Tests handling of WINDOW_UPDATE frame with incorrect size. @@ -4150,8 +4166,9 @@ EXPECT_EQ(SpdyFramer::SPDY_ERROR, visitor.framer_.state()); EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE, - visitor.framer_.error_code()) - << SpdyFramer::ErrorCodeToString(visitor.framer_.error_code()); + visitor.framer_.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString( + visitor.framer_.spdy_framer_error()); } // Tests handling of RST_STREAM frame with incorrect size. @@ -4170,8 +4187,32 @@ EXPECT_EQ(SpdyFramer::SPDY_ERROR, visitor.framer_.state()); EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE, - visitor.framer_.error_code()) - << SpdyFramer::ErrorCodeToString(visitor.framer_.error_code()); + visitor.framer_.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString( + visitor.framer_.spdy_framer_error()); +} + +// Regression test for https://crbug.com/548674: +// RST_STREAM with payload must not be accepted. +TEST_P(SpdyFramerTest, ReadInvalidRstStreamWithPayload) { + const unsigned char kFrameData[] = { + 0x00, 0x00, 0x07, // Length: 7 + 0x03, // Type: RST_STREAM + 0x00, // Flags: none + 0x00, 0x00, 0x00, 0x01, // Stream: 1 + 0x00, 0x00, 0x00, 0x00, // Error: NO_ERROR + 'f', 'o', 'o' // Payload: "foo" + }; + + TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); + visitor.SimulateInFramer(kFrameData, sizeof(kFrameData)); + + EXPECT_EQ(SpdyFramer::SPDY_ERROR, visitor.framer_.state()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE, + visitor.framer_.spdy_framer_error()) + << SpdyFramer::SpdyFramerErrorToString( + visitor.framer_.spdy_framer_error()); + EXPECT_TRUE(visitor.fin_opaque_data_.empty()); } // Test that SpdyFramer processes, by default, all passed input in one call
diff --git a/net/spdy/spdy_no_op_visitor.cc b/net/spdy/spdy_no_op_visitor.cc index 60a6379..ddf3f6e 100644 --- a/net/spdy/spdy_no_op_visitor.cc +++ b/net/spdy/spdy_no_op_visitor.cc
@@ -20,7 +20,8 @@ return this; } -bool SpdyNoOpVisitor::OnUnknownFrame(SpdyStreamId stream_id, int frame_type) { +bool SpdyNoOpVisitor::OnUnknownFrame(SpdyStreamId stream_id, + uint8_t frame_type) { return true; }
diff --git a/net/spdy/spdy_no_op_visitor.h b/net/spdy/spdy_no_op_visitor.h index cd3d495..b06f732 100644 --- a/net/spdy/spdy_no_op_visitor.h +++ b/net/spdy/spdy_no_op_visitor.h
@@ -9,6 +9,8 @@ #ifndef NET_SPDY_SPDY_NO_OP_VISITOR_H_ #define NET_SPDY_SPDY_NO_OP_VISITOR_H_ +#include <cstdint> + #include "net/spdy/spdy_framer.h" #include "net/spdy/spdy_protocol.h" @@ -63,7 +65,7 @@ SpdyStreamId parent_stream_id, int weight, bool exclusive) override {} - bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override; + bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override; // SpdyFramerDebugVisitorInterface methods: void OnSendCompressedFrame(SpdyStreamId stream_id,
diff --git a/net/spdy/spdy_protocol.cc b/net/spdy/spdy_protocol.cc index 079189f1..3537917e 100644 --- a/net/spdy/spdy_protocol.cc +++ b/net/spdy/spdy_protocol.cc
@@ -59,39 +59,6 @@ return static_cast<SpdyFrameType>(frame_type_field); } -int SerializeFrameType(SpdyFrameType frame_type) { - switch (frame_type) { - case DATA: - return kDataFrameType; - case HEADERS: - return 1; - case PRIORITY: - return 2; - case RST_STREAM: - return 3; - case SETTINGS: - return 4; - case PUSH_PROMISE: - return 5; - case PING: - return 6; - case GOAWAY: - return 7; - case WINDOW_UPDATE: - return 8; - case CONTINUATION: - return 9; - // ALTSVC and BLOCKED are extensions. - case ALTSVC: - return 10; - case BLOCKED: - return 11; - default: - SPDY_BUG << "Serializing unhandled frame type " << frame_type; - return -1; - } -} - bool IsValidHTTP2FrameStreamId(SpdyStreamId current_frame_stream_id, SpdyFrameType frame_type_field) { if (current_frame_stream_id == 0) { @@ -170,39 +137,6 @@ return static_cast<SpdyRstStreamStatus>(rst_stream_status_field); } -int SerializeRstStreamStatus(SpdyRstStreamStatus rst_stream_status) { - // TODO(bnc): Simplify this method. - switch (rst_stream_status) { - case RST_STREAM_NO_ERROR: - return 0; - case RST_STREAM_PROTOCOL_ERROR: - return 1; - case RST_STREAM_INTERNAL_ERROR: - return 2; - case RST_STREAM_FLOW_CONTROL_ERROR: - return 3; - case RST_STREAM_STREAM_CLOSED: - return 5; - case RST_STREAM_FRAME_SIZE_ERROR: - return 6; - case RST_STREAM_REFUSED_STREAM: - return 7; - case RST_STREAM_CANCEL: - return 8; - case RST_STREAM_CONNECT_ERROR: - return 10; - case RST_STREAM_ENHANCE_YOUR_CALM: - return 11; - case RST_STREAM_INADEQUATE_SECURITY: - return 12; - case RST_STREAM_HTTP_1_1_REQUIRED: - return 13; - default: - SPDY_BUG << "Unhandled RST_STREAM status " << rst_stream_status; - return -1; - } -} - SpdyGoAwayStatus ParseGoAwayStatus(int goaway_status_field) { if (goaway_status_field < GOAWAY_MIN || goaway_status_field > GOAWAY_MAX) { return GOAWAY_INTERNAL_ERROR; @@ -211,43 +145,6 @@ return static_cast<SpdyGoAwayStatus>(goaway_status_field); } -int SerializeGoAwayStatus(SpdyGoAwayStatus status) { - // TODO(bnc): Simplify this method. - switch (status) { - case GOAWAY_NO_ERROR: - return 0; - case GOAWAY_PROTOCOL_ERROR: - return 1; - case GOAWAY_INTERNAL_ERROR: - return 2; - case GOAWAY_FLOW_CONTROL_ERROR: - return 3; - case GOAWAY_SETTINGS_TIMEOUT: - return 4; - case GOAWAY_STREAM_CLOSED: - return 5; - case GOAWAY_FRAME_SIZE_ERROR: - return 6; - case GOAWAY_REFUSED_STREAM: - return 7; - case GOAWAY_CANCEL: - return 8; - case GOAWAY_COMPRESSION_ERROR: - return 9; - case GOAWAY_CONNECT_ERROR: - return 10; - case GOAWAY_ENHANCE_YOUR_CALM: - return 11; - case GOAWAY_INADEQUATE_SECURITY: - return 12; - case GOAWAY_HTTP_1_1_REQUIRED: - return 13; - default: - SPDY_BUG << "Serializing unhandled GOAWAY status " << status; - return -1; - } -} - const char* const kHttp2Npn = "h2"; SpdyFrameWithHeaderBlockIR::SpdyFrameWithHeaderBlockIR(
diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h index 4a6d7759..642efb4 100644 --- a/net/spdy/spdy_protocol.h +++ b/net/spdy/spdy_protocol.h
@@ -233,10 +233,6 @@ // use IsValidFrameType() to verify validity of frame type fields. NET_EXPORT_PRIVATE SpdyFrameType ParseFrameType(int frame_type_field); -// Serializes a given frame type to the on-the-wire enumeration value. -// Returns -1 on failure (I.E. Invalid frame type). -NET_EXPORT_PRIVATE int SerializeFrameType(SpdyFrameType frame_type); - // (HTTP/2) All standard frame types except WINDOW_UPDATE are // (stream-specific xor connection-level). Returns false iff we know // the given frame type does not align with the given streamID. @@ -260,21 +256,11 @@ NET_EXPORT_PRIVATE SpdyRstStreamStatus ParseRstStreamStatus(int rst_stream_status_field); -// Serializes a given RST_STREAM status code to the on-the-wire enumeration -// value. Returns -1 on failure (I.E. Invalid RST_STREAM status code for the -// given version). -NET_EXPORT_PRIVATE int SerializeRstStreamStatus( - SpdyRstStreamStatus rst_stream_status); - // Parses a GOAWAY error code from an on-the-wire enumeration. // Treat unrecognized error codes as INTERNAL_ERROR // as recommended by the HTTP/2 specification. NET_EXPORT_PRIVATE SpdyGoAwayStatus ParseGoAwayStatus(int goaway_status_field); -// Serializes a given GOAWAY status to the on-the-wire enumeration value. -// Returns -1 on failure (I.E. Invalid GOAWAY status for the given version). -NET_EXPORT_PRIVATE int SerializeGoAwayStatus(SpdyGoAwayStatus status); - // Frame type for non-control (i.e. data) frames. const int kDataFrameType = 0; // Number of octets in the frame header.
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index 55c0c47..a7287cd 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc
@@ -370,7 +370,7 @@ } // namespace SpdyProtocolErrorDetails MapFramerErrorToProtocolError( - SpdyFramer::SpdyError err) { + SpdyFramer::SpdyFramerError err) { switch (err) { case SpdyFramer::SPDY_NO_ERROR: return SPDY_ERROR_NO_ERROR; @@ -412,7 +412,7 @@ } } -Error MapFramerErrorToNetError(SpdyFramer::SpdyError err) { +Error MapFramerErrorToNetError(SpdyFramer::SpdyFramerError err) { switch (err) { case SpdyFramer::SPDY_NO_ERROR: return OK; @@ -1482,7 +1482,8 @@ return ERR_CONNECTION_CLOSED; } - DCHECK_EQ(buffered_spdy_framer_->error_code(), SpdyFramer::SPDY_NO_ERROR); + DCHECK_EQ(buffered_spdy_framer_->spdy_framer_error(), + SpdyFramer::SPDY_NO_ERROR); } read_state_ = READ_STATE_DO_READ; @@ -2039,15 +2040,15 @@ return ssl_socket->GetTokenBindingSignature(key, tb_type, out); } -void SpdySession::OnError(SpdyFramer::SpdyError error_code) { +void SpdySession::OnError(SpdyFramer::SpdyFramerError spdy_framer_error) { CHECK(in_io_loop_); - RecordProtocolErrorHistogram(MapFramerErrorToProtocolError(error_code)); - std::string description = - base::StringPrintf("Framer error: %d (%s).", - error_code, - SpdyFramer::ErrorCodeToString(error_code)); - DoDrainSession(MapFramerErrorToNetError(error_code), description); + RecordProtocolErrorHistogram( + MapFramerErrorToProtocolError(spdy_framer_error)); + std::string description = base::StringPrintf( + "Framer error: %d (%s).", spdy_framer_error, + SpdyFramer::SpdyFramerErrorToString(spdy_framer_error)); + DoDrainSession(MapFramerErrorToNetError(spdy_framer_error), description); } void SpdySession::OnStreamError(SpdyStreamId stream_id, @@ -2354,7 +2355,7 @@ scheme_host_port, alternative_service_info_vector); } -bool SpdySession::OnUnknownFrame(SpdyStreamId stream_id, int frame_type) { +bool SpdySession::OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) { // Validate stream id. // Was the frame sent on a stream id that has not been used in this session? if (stream_id % 2 == 1 && stream_id > stream_hi_water_mark_)
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h index 1e3244e3..fb61ec7 100644 --- a/net/spdy/spdy_session.h +++ b/net/spdy/spdy_session.h
@@ -90,7 +90,7 @@ // NOTE: There's an enum of the same name (also with numeric suffixes) // in histograms.xml. Be sure to add new values there also. enum SpdyProtocolErrorDetails { - // SpdyFramer::SpdyError mappings. + // SpdyFramer::SpdyFramerError mappings. SPDY_ERROR_NO_ERROR = 0, SPDY_ERROR_INVALID_STREAM_ID = 38, SPDY_ERROR_INVALID_CONTROL_FRAME = 1, @@ -141,8 +141,9 @@ NUM_SPDY_PROTOCOL_ERROR_DETAILS = 43, }; SpdyProtocolErrorDetails NET_EXPORT_PRIVATE - MapFramerErrorToProtocolError(SpdyFramer::SpdyError error); -Error NET_EXPORT_PRIVATE MapFramerErrorToNetError(SpdyFramer::SpdyError error); +MapFramerErrorToProtocolError(SpdyFramer::SpdyFramerError error); +Error NET_EXPORT_PRIVATE +MapFramerErrorToNetError(SpdyFramer::SpdyFramerError error); SpdyProtocolErrorDetails NET_EXPORT_PRIVATE MapRstStreamStatusToProtocolError(SpdyRstStreamStatus status); SpdyGoAwayStatus NET_EXPORT_PRIVATE MapNetErrorToGoAwayStatus(Error err); @@ -856,7 +857,7 @@ void DeleteExpiredPushedStreams(); // BufferedSpdyFramerVisitorInterface: - void OnError(SpdyFramer::SpdyError error_code) override; + void OnError(SpdyFramer::SpdyFramerError spdy_framer_error) override; void OnStreamError(SpdyStreamId stream_id, const std::string& description) override; void OnPing(SpdyPingId unique_id, bool is_ack) override; @@ -889,7 +890,7 @@ base::StringPiece origin, const SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector) override; - bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override; + bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override; // SpdyFramerDebugVisitorInterface void OnSendCompressedFrame(SpdyStreamId stream_id,
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc index 48848b5..d2a9c71f 100644 --- a/net/spdy/spdy_test_util_common.cc +++ b/net/spdy/spdy_test_util_common.cc
@@ -188,7 +188,7 @@ return priority_; } - void OnError(SpdyFramer::SpdyError error_code) override {} + void OnError(SpdyFramer::SpdyFramerError spdy_framer_error) override {} void OnStreamError(SpdyStreamId stream_id, const std::string& description) override {} void OnHeaders(SpdyStreamId stream_id, @@ -226,7 +226,7 @@ base::StringPiece origin, const SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector) override {} - bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override { + bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override { return false; }
diff --git a/net/ssl/channel_id_service.cc b/net/ssl/channel_id_service.cc index 74ab3816..d487346 100644 --- a/net/ssl/channel_id_service.cc +++ b/net/ssl/channel_id_service.cc
@@ -18,11 +18,11 @@ #include "base/logging.h" #include "base/macros.h" #include "base/memory/ptr_util.h" -#include "base/memory/ref_counted.h" #include "base/metrics/histogram_macros.h" #include "base/rand_util.h" #include "base/single_thread_task_runner.h" #include "base/task_runner.h" +#include "base/task_scheduler/post_task.h" #include "base/threading/thread_task_runner_handle.h" #include "crypto/ec_private_key.h" #include "net/base/net_errors.h" @@ -110,9 +110,8 @@ } // namespace -// ChannelIDServiceWorker runs on a worker thread and takes care of the -// blocking process of performing key generation. Will take care of deleting -// itself once Start() is called. +// ChannelIDServiceWorker takes care of the blocking process of performing key +// generation. Will take care of deleting itself once Start() is called. class ChannelIDServiceWorker { public: typedef base::Callback< @@ -125,15 +124,22 @@ origin_task_runner_(base::ThreadTaskRunnerHandle::Get()), callback_(callback) {} - // Starts the worker on |task_runner|. If the worker fails to start, such as - // if the task runner is shutting down, then it will take care of deleting - // itself. - bool Start(const scoped_refptr<base::TaskRunner>& task_runner) { + // Starts the worker asynchronously. + void Start(const scoped_refptr<base::TaskRunner>& task_runner) { DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); - return task_runner->PostTask( - FROM_HERE, - base::Bind(&ChannelIDServiceWorker::Run, base::Owned(this))); + auto callback = base::Bind(&ChannelIDServiceWorker::Run, base::Owned(this)); + + if (task_runner) { + task_runner->PostTask(FROM_HERE, callback); + } else { + base::PostTaskWithTraits( + FROM_HERE, base::TaskTraits() + .WithShutdownBehavior( + base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN) + .MayBlock(), + callback); + } } private: @@ -269,11 +275,8 @@ base::ResetAndReturn(&callback_).Run(error); } -ChannelIDService::ChannelIDService( - ChannelIDStore* channel_id_store, - const scoped_refptr<base::TaskRunner>& task_runner) +ChannelIDService::ChannelIDService(ChannelIDStore* channel_id_store) : channel_id_store_(channel_id_store), - task_runner_(task_runner), id_(g_next_id.GetNext()), requests_(0), key_store_hits_(0), @@ -332,12 +335,8 @@ domain, base::Bind(&ChannelIDService::GeneratedChannelID, weak_ptr_factory_.GetWeakPtr())); - if (!worker->Start(task_runner_)) { - // TODO(rkn): Log to the NetLog. - LOG(ERROR) << "ChannelIDServiceWorker couldn't be started."; - RecordGetChannelIDResult(WORKER_FAILURE); - return ERR_INSUFFICIENT_RESOURCES; - } + worker->Start(task_runner_); + // We are waiting for key generation. Create a job & request to track it. ChannelIDServiceJob* job = new ChannelIDServiceJob(create_if_missing); inflight_[domain] = base::WrapUnique(job); @@ -415,11 +414,7 @@ server_identifier, base::Bind(&ChannelIDService::GeneratedChannelID, weak_ptr_factory_.GetWeakPtr())); - if (!worker->Start(task_runner_)) { - // TODO(rkn): Log to the NetLog. - LOG(ERROR) << "ChannelIDServiceWorker couldn't be started."; - HandleResult(ERR_INSUFFICIENT_RESOURCES, server_identifier, nullptr); - } + worker->Start(task_runner_); } ChannelIDStore* ChannelIDService::GetChannelIDStore() {
diff --git a/net/ssl/channel_id_service.h b/net/ssl/channel_id_service.h index 617a297..31b3022 100644 --- a/net/ssl/channel_id_service.h +++ b/net/ssl/channel_id_service.h
@@ -10,9 +10,11 @@ #include <map> #include <memory> #include <string> +#include <utility> #include <vector> #include "base/macros.h" +#include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/threading/non_thread_safe.h" #include "base/time/time.h" @@ -69,15 +71,17 @@ ChannelIDServiceJob* job_; }; - // This object owns |channel_id_store|. |task_runner| will - // be used to post channel ID generation worker tasks. The tasks are - // safe for use with WorkerPool and SequencedWorkerPool::CONTINUE_ON_SHUTDOWN. - ChannelIDService( - ChannelIDStore* channel_id_store, - const scoped_refptr<base::TaskRunner>& task_runner); + // This object owns |channel_id_store|. + explicit ChannelIDService(ChannelIDStore* channel_id_store); ~ChannelIDService(); + // Sets the TaskRunner to use for asynchronous operations. + void set_task_runner_for_testing( + scoped_refptr<base::TaskRunner> task_runner) { + task_runner_ = std::move(task_runner); + } + // Returns the domain to be used for |host|. The domain is the // "registry controlled domain", or the "ETLD + 1" where one exists, or // the origin otherwise.
diff --git a/net/ssl/channel_id_service_unittest.cc b/net/ssl/channel_id_service_unittest.cc index dbb41fc..8cdd7e00 100644 --- a/net/ssl/channel_id_service_unittest.cc +++ b/net/ssl/channel_id_service_unittest.cc
@@ -11,10 +11,13 @@ #include "base/bind.h" #include "base/location.h" #include "base/macros.h" +#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" #include "base/task_runner.h" +#include "base/task_scheduler/task_scheduler.h" +#include "base/test/scoped_task_scheduler.h" #include "base/threading/thread_task_runner_handle.h" #include "crypto/ec_private_key.h" #include "net/base/net_errors.h" @@ -38,28 +41,6 @@ FAIL(); } -// Simple task runner that refuses to actually post any tasks. This simulates -// a TaskRunner that has been shutdown, by returning false for any attempt to -// add new tasks. -class FailingTaskRunner : public base::TaskRunner { - public: - FailingTaskRunner() {} - - bool PostDelayedTask(const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeDelta delay) override { - return false; - } - - bool RunsTasksOnCurrentThread() const override { return true; } - - protected: - ~FailingTaskRunner() override {} - - private: - DISALLOW_COPY_AND_ASSIGN(FailingTaskRunner); -}; - class MockChannelIDStoreWithAsyncGet : public DefaultChannelIDStore { public: @@ -111,10 +92,11 @@ class ChannelIDServiceTest : public testing::Test { public: ChannelIDServiceTest() - : service_(new ChannelIDService(new DefaultChannelIDStore(NULL), - base::ThreadTaskRunnerHandle::Get())) {} + : scoped_task_scheduler_(base::MessageLoop::current()), + service_(new ChannelIDService(new DefaultChannelIDStore(NULL))) {} protected: + base::test::ScopedTaskScheduler scoped_task_scheduler_; std::unique_ptr<ChannelIDService> service_; }; @@ -376,13 +358,12 @@ // If we got here without crashing or a valgrind error, it worked. } -// Tests that shutting down the sequenced worker pool and then making new -// requests gracefully fails. +// Tests that shutting down the TaskScheduler and then making new requests +// gracefully fails. // This is a regression test for http://crbug.com/236387 TEST_F(ChannelIDServiceTest, RequestAfterPoolShutdown) { - scoped_refptr<FailingTaskRunner> task_runner(new FailingTaskRunner); - service_.reset(new ChannelIDService( - new DefaultChannelIDStore(NULL), task_runner)); + // Drop all tasks posted to TaskScheduler from now on. + base::TaskScheduler::GetInstance()->Shutdown(); // Make a request that will force synchronous completion. std::string host("encrypted.google.com"); @@ -393,8 +374,8 @@ error = service_->GetOrCreateChannelID(host, &key, base::Bind(&FailTest), &request); // If we got here without crashing or a valgrind error, it worked. - ASSERT_THAT(error, IsError(ERR_INSUFFICIENT_RESOURCES)); - EXPECT_FALSE(request.is_active()); + ASSERT_THAT(error, IsError(ERR_IO_PENDING)); + EXPECT_TRUE(request.is_active()); } // Tests that simultaneous creation of different certs works. @@ -453,8 +434,8 @@ TEST_F(ChannelIDServiceTest, AsyncStoreGetOrCreateNoChannelIDsInStore) { MockChannelIDStoreWithAsyncGet* mock_store = new MockChannelIDStoreWithAsyncGet(); - service_ = std::unique_ptr<ChannelIDService>( - new ChannelIDService(mock_store, base::ThreadTaskRunnerHandle::Get())); + service_ = + std::unique_ptr<ChannelIDService>(new ChannelIDService(mock_store)); std::string host("encrypted.google.com"); @@ -482,8 +463,8 @@ TEST_F(ChannelIDServiceTest, AsyncStoreGetNoChannelIDsInStore) { MockChannelIDStoreWithAsyncGet* mock_store = new MockChannelIDStoreWithAsyncGet(); - service_ = std::unique_ptr<ChannelIDService>( - new ChannelIDService(mock_store, base::ThreadTaskRunnerHandle::Get())); + service_ = + std::unique_ptr<ChannelIDService>(new ChannelIDService(mock_store)); std::string host("encrypted.google.com"); @@ -511,8 +492,8 @@ TEST_F(ChannelIDServiceTest, AsyncStoreGetOrCreateOneCertInStore) { MockChannelIDStoreWithAsyncGet* mock_store = new MockChannelIDStoreWithAsyncGet(); - service_ = std::unique_ptr<ChannelIDService>( - new ChannelIDService(mock_store, base::ThreadTaskRunnerHandle::Get())); + service_ = + std::unique_ptr<ChannelIDService>(new ChannelIDService(mock_store)); std::string host("encrypted.google.com"); @@ -548,8 +529,8 @@ TEST_F(ChannelIDServiceTest, AsyncStoreGetOneCertInStore) { MockChannelIDStoreWithAsyncGet* mock_store = new MockChannelIDStoreWithAsyncGet(); - service_ = std::unique_ptr<ChannelIDService>( - new ChannelIDService(mock_store, base::ThreadTaskRunnerHandle::Get())); + service_ = + std::unique_ptr<ChannelIDService>(new ChannelIDService(mock_store)); std::string host("encrypted.google.com"); @@ -584,8 +565,8 @@ TEST_F(ChannelIDServiceTest, AsyncStoreGetThenCreateNoCertsInStore) { MockChannelIDStoreWithAsyncGet* mock_store = new MockChannelIDStoreWithAsyncGet(); - service_ = std::unique_ptr<ChannelIDService>( - new ChannelIDService(mock_store, base::ThreadTaskRunnerHandle::Get())); + service_ = + std::unique_ptr<ChannelIDService>(new ChannelIDService(mock_store)); std::string host("encrypted.google.com");
diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc index 4e7c7bc4..f21b0f40b 100644 --- a/net/url_request/url_request_context_builder.cc +++ b/net/url_request/url_request_context_builder.cc
@@ -356,10 +356,8 @@ } else { std::unique_ptr<CookieStore> cookie_store( new CookieMonster(nullptr, nullptr)); - // TODO(mmenke): This always creates a file thread, even when it ends up - // not being used. Consider lazily creating the thread. - std::unique_ptr<ChannelIDService> channel_id_service(new ChannelIDService( - new DefaultChannelIDStore(NULL), context->GetFileTaskRunner())); + std::unique_ptr<ChannelIDService> channel_id_service( + new ChannelIDService(new DefaultChannelIDStore(NULL))); cookie_store->SetChannelIDServiceID(channel_id_service->GetUniqueID()); storage->set_cookie_store(std::move(cookie_store)); storage->set_channel_id_service(std::move(channel_id_service));
diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc index 97ccc71..1e905fd 100644 --- a/net/url_request/url_request_test_util.cc +++ b/net/url_request/url_request_test_util.cc
@@ -14,7 +14,6 @@ #include "base/single_thread_task_runner.h" #include "base/threading/thread.h" #include "base/threading/thread_task_runner_handle.h" -#include "base/threading/worker_pool.h" #include "net/base/host_port_pair.h" #include "net/cert/cert_verifier.h" #include "net/cert/ct_policy_enforcer.h" @@ -108,9 +107,8 @@ // In-memory Channel ID service. Must be created before the // HttpNetworkSession. if (!channel_id_service()) { - context_storage_.set_channel_id_service(base::MakeUnique<ChannelIDService>( - new DefaultChannelIDStore(nullptr), - base::WorkerPool::GetTaskRunner(true))); + context_storage_.set_channel_id_service( + base::MakeUnique<ChannelIDService>(new DefaultChannelIDStore(nullptr))); } if (http_transaction_factory()) { // Make sure we haven't been passed an object we're not going to use.
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index a9d1c68..dd2b713 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc
@@ -43,6 +43,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/test/histogram_tester.h" +#include "base/test/scoped_task_scheduler.h" #include "base/threading/thread_task_runner_handle.h" #include "base/values.h" #include "net/base/chunked_upload_data_stream.h" @@ -3443,16 +3444,21 @@ #if !defined(OS_IOS) class TokenBindingURLRequestTest : public URLRequestTestHTTP { public: + TokenBindingURLRequestTest() + : scoped_task_scheduler_(base::MessageLoop::current()) {} + void SetUp() override { default_context_.set_ssl_config_service( new TestSSLConfigService(false, false, false, true)); - channel_id_service_.reset(new ChannelIDService( - new DefaultChannelIDStore(NULL), base::ThreadTaskRunnerHandle::Get())); + channel_id_service_.reset( + new ChannelIDService(new DefaultChannelIDStore(NULL))); default_context_.set_channel_id_service(channel_id_service_.get()); URLRequestTestHTTP::SetUp(); } protected: + // Required by ChannelIDService. + base::test::ScopedTaskScheduler scoped_task_scheduler_; std::unique_ptr<ChannelIDService> channel_id_service_; }; @@ -8531,13 +8537,17 @@ class HTTPSRequestTest : public testing::Test { public: - HTTPSRequestTest() : default_context_(true) { + HTTPSRequestTest() + : scoped_task_scheduler_(base::MessageLoop::current()), + default_context_(true) { default_context_.set_network_delegate(&default_network_delegate_); default_context_.Init(); } ~HTTPSRequestTest() override {} protected: + // Required by ChannelIDService. + base::test::ScopedTaskScheduler scoped_task_scheduler_; TestNetworkDelegate default_network_delegate_; // Must outlive URLRequest. TestURLRequestContext default_context_; }; @@ -9154,7 +9164,9 @@ class HTTPSSessionTest : public testing::Test { public: - HTTPSSessionTest() : default_context_(true) { + HTTPSSessionTest() + : scoped_task_scheduler_(base::MessageLoop::current()), + default_context_(true) { cert_verifier_.set_default_result(OK); default_context_.set_network_delegate(&default_network_delegate_); @@ -9164,6 +9176,8 @@ ~HTTPSSessionTest() override {} protected: + // Required by ChannelIDService. + base::test::ScopedTaskScheduler scoped_task_scheduler_; MockCertVerifier cert_verifier_; TestNetworkDelegate default_network_delegate_; // Must outlive URLRequest. TestURLRequestContext default_context_;
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index eeeca72..74bff0c5 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -1139,7 +1139,7 @@ ], "experiments": [ { - "name": "Enabled" + "name": "EnabledFull" } ] }
diff --git a/third_party/WebKit/API_OWNERS b/third_party/WebKit/API_OWNERS index 6cfcc30..f503683 100644 --- a/third_party/WebKit/API_OWNERS +++ b/third_party/WebKit/API_OWNERS
@@ -1,7 +1,7 @@ # Blink API owners are responsible for decisions about what APIs Blink should # expose to the open web. # -# See http://www.chromium.org/blink#new-features for details. +# See https://www.chromium.org/blink#new-features for details. chrishtr@chromium.org darin@chromium.org dglazkov@chromium.org
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/root-layer-scrolls b/third_party/WebKit/LayoutTests/FlagExpectations/root-layer-scrolls index 14356a6..df2d5a1 100644 --- a/third_party/WebKit/LayoutTests/FlagExpectations/root-layer-scrolls +++ b/third_party/WebKit/LayoutTests/FlagExpectations/root-layer-scrolls
@@ -356,15 +356,10 @@ Bug(none) fast/scroll-behavior/subframe-scrollLeft.html [ Failure ] Bug(none) fast/scroll-behavior/subframe-scrollTo.html [ Failure ] Bug(none) fast/scroll-behavior/subframe-scrollTop.html [ Failure ] -Bug(none) fast/scrolling/scrollable-area-frame-overflow-hidden.html [ Failure ] -Bug(none) fast/scrolling/scrollable-area-frame-overried-inherited-visibility-hidden.html [ Failure ] Bug(none) fast/scrolling/scrollable-area-frame-scrolling-no-overried-inherited-visibility-hidden.html [ Failure ] Bug(none) fast/scrolling/scrollable-area-frame-scrolling-no-visibility-hidden-child.html [ Failure ] Bug(none) fast/scrolling/scrollable-area-frame-scrolling-no.html [ Failure ] -Bug(none) fast/scrolling/scrollable-area-frame-scrolling-yes.html [ Failure ] -Bug(none) fast/scrolling/scrollable-area-frame-visibility-hidden-child.html [ Failure ] Bug(none) fast/scrolling/scrollable-area-frame-zero-size-and-border.html [ Failure ] -Bug(none) fast/scrolling/scrollable-area-frame.html [ Failure ] Bug(none) fast/scrolling/scrollbar-tickmarks-styled.html [ Failure ] Bug(none) fast/scrolling/scrollbar-tickmarks-styled-after-onload.html [ Failure ] Bug(none) fast/selectors/166.html [ Failure ] @@ -438,7 +433,6 @@ Bug(none) printing/page-count-relayout-shrink.html [ Failure ] Bug(none) printing/return-from-printing-mode.html [ Failure ] Bug(none) scrollbars/custom-scrollbar-changing-style.html [ Failure ] -Bug(none) scrollingcoordinator/non-fast-scrollable-visibility-change.html [ Failure ] Bug(none) svg/custom/getscreenctm-in-scrollable-svg-area.xhtml [ Failure ] Bug(none) svg/custom/junk-data.svg [ Failure ] Bug(none) svg/custom/load-non-wellformed.svg [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations index 0e3ecb28..9d23ec56 100644 --- a/third_party/WebKit/LayoutTests/TestExpectations +++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -936,13 +936,6 @@ crbug.com/655963 inspector/console/console-dir.html [ NeedsManualRebaseline ] -crbug.com/665820 http/tests/serviceworker/clients-matchall-client-types.html [ Pass Failure ] -crbug.com/665820 http/tests/serviceworker/clients-get-client-types.html [ Pass Failure ] -crbug.com/665820 virtual/mojo-loading/http/tests/serviceworker/clients-matchall-client-types.html [ Pass Failure ] -crbug.com/665820 virtual/mojo-loading/http/tests/serviceworker/clients-get-client-types.html [ Pass Failure ] -crbug.com/665820 virtual/service-worker-navigation-preload/http/tests/serviceworker/clients-get-client-types.html [ Pass Failure ] -crbug.com/665820 virtual/service-worker-navigation-preload/http/tests/serviceworker/clients-matchall-client-types.html [ Pass Failure ] - crbug.com/405389 external/csswg-test/css-shapes-1/shape-outside/supported-shapes/polygon/shape-outside-polygon-017.html [ Failure ] crbug.com/424365 external/csswg-test/css-shapes-1/shape-outside/shape-image/shape-image-010.html [ Failure ] crbug.com/424365 external/csswg-test/css-shapes-1/shape-outside/shape-image/shape-image-024.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/W3CImportExpectations b/third_party/WebKit/LayoutTests/W3CImportExpectations index 31fce61..ac50f964 100644 --- a/third_party/WebKit/LayoutTests/W3CImportExpectations +++ b/third_party/WebKit/LayoutTests/W3CImportExpectations
@@ -661,8 +661,12 @@ external/wpt/html/semantics/forms/form-submission-0/url-encoded.html [ Skip ] external/wpt/svg/linking/scripted [ Skip ] -# crbug.com/656171: w3c-test-importer fails to import file with non-valid utf8 (004.worker.js). +# Importer fails to import some files with some non-utf8 encodings. crbug.com/656171 external/wpt/workers/semantics/encodings [ Skip ] +crbug.com/656171 external/wpt/html/semantics/scripting-1/the-script-element/external-script-windows1250.js [ Skip ] +crbug.com/656171 external/wpt/html/semantics/scripting-1/the-script-element/script-charset-01.html [ Skip ] +crbug.com/656171 external/wpt/html/semantics/scripting-1/the-script-element/script-charset-02.html [ Skip ] +crbug.com/656171 external/wpt/html/semantics/scripting-1/the-script-element/script-charset-03.html [ Skip ] # They have a wrong encoding, and test_parser.py throws UnicodeEncodeError. external/wpt/domxpath/001.html [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/animations/interpolation/transform-interpolation.html b/third_party/WebKit/LayoutTests/animations/interpolation/transform-interpolation.html index 318436a..3e9960f7 100644 --- a/third_party/WebKit/LayoutTests/animations/interpolation/transform-interpolation.html +++ b/third_party/WebKit/LayoutTests/animations/interpolation/transform-interpolation.html
@@ -589,6 +589,18 @@ {at: 1, is: 'matrix3d(1, 0, 0, 0, -4.370079726523038, 2, 0, 0, 0, 0, 1, 0, 4, -12, 8, 1)'}, {at: 2, is: 'matrix3d(1, 0, 0, 0, -17.782462353533823, 3, 0, 0, 0.021237113402061854, -0.010618556701030927, 1.03, 0.0026546391752577322, 0, -20, 4, 0.9793814432989691)'}, ]); +assertInterpolation({ + property: 'transform', + from: 'translate3D(100px, 200px, 300px)', + to: 'none' +}, [ + {at: -1, is: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 200, 400, 600, 1)'}, + {at: 0, is: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 100, 200, 300, 1)'}, + {at: 0.25, is: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 75, 150, 225, 1)'}, + {at: 0.75, is: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 25, 50, 75, 1)'}, + {at: 1, is: 'matrix(1, 0, 0, 1, 0, 0) '}, + {at: 2, is: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -100, -200, -300, 1)'}, +]); // Matrix assertInterpolation({
diff --git a/third_party/WebKit/LayoutTests/external/csswg-test/css-ui-3/caret-color-016.html b/third_party/WebKit/LayoutTests/external/csswg-test/css-ui-3/caret-color-016.html index 9de1f339..25740ace 100644 --- a/third_party/WebKit/LayoutTests/external/csswg-test/css-ui-3/caret-color-016.html +++ b/third_party/WebKit/LayoutTests/external/csswg-test/css-ui-3/caret-color-016.html
@@ -30,11 +30,20 @@ } </style> <body> - <p>Before running this test, the link below must have been visited. It will have yellow text if this is not the case. If it its text is yellow, you need to navigate to this link first. - <p><a id="link" contenteditable href="caret-color-016.html">link</a></p> + <p>Before running this test, this <a href="./">link</a> must have been visited. It will have yellow text if this is not the case. If it its text is yellow, you need to navigate to this link first. + <p><a id="link" contenteditable href="./">link</a></p> <div id=log></div> <script> + setup( + function(){ + /* Helper to get the link into the browsing history. + Using a relative path because some browsers only allow replaceState within the same domain. */ + current_url = window.location.href; + history.replaceState({},"","./"); + history.replaceState({},"",current_url); + }); + test( function(){ var link = document.getElementById("link");
diff --git a/third_party/WebKit/LayoutTests/external/csswg-test/css-writing-modes-3/block-flow-direction-vrl-021.xht b/third_party/WebKit/LayoutTests/external/csswg-test/css-writing-modes-3/block-flow-direction-vrl-021.xht index a4e8c477..daec06c 100644 --- a/third_party/WebKit/LayoutTests/external/csswg-test/css-writing-modes-3/block-flow-direction-vrl-021.xht +++ b/third_party/WebKit/LayoutTests/external/csswg-test/css-writing-modes-3/block-flow-direction-vrl-021.xht
@@ -38,7 +38,7 @@ border-bottom: blue solid 1em; list-style: none outside url("support/blue1x1.png"); margin: 0em; - padding-top: 1em; /* overriding default -webkit-padding-start: 40px in several browsers */ + padding-top: 1em; /* overriding default padding-start: 40px in several browsers */ } ul.right-border
diff --git a/third_party/WebKit/LayoutTests/external/csswg-test/css-writing-modes-3/line-box-direction-vlr-020.xht b/third_party/WebKit/LayoutTests/external/csswg-test/css-writing-modes-3/line-box-direction-vlr-020.xht index c1573eb..7c6f2a7 100644 --- a/third_party/WebKit/LayoutTests/external/csswg-test/css-writing-modes-3/line-box-direction-vlr-020.xht +++ b/third_party/WebKit/LayoutTests/external/csswg-test/css-writing-modes-3/line-box-direction-vlr-020.xht
@@ -27,7 +27,7 @@ height: 7em; /* Each line box has an inline-size of 7em */ list-style: none outside none; margin: 0em; - padding-top: 0em; /* overriding default -webkit-padding-start: 40px in several browsers */ + padding-top: 0em; /* overriding default padding-start: 40px in several browsers */ writing-mode: vertical-lr; } ]]></style>
diff --git a/third_party/WebKit/LayoutTests/external/csswg-test/css-writing-modes-3/line-box-direction-vrl-019.xht b/third_party/WebKit/LayoutTests/external/csswg-test/css-writing-modes-3/line-box-direction-vrl-019.xht index d060943c..774bf92 100644 --- a/third_party/WebKit/LayoutTests/external/csswg-test/css-writing-modes-3/line-box-direction-vrl-019.xht +++ b/third_party/WebKit/LayoutTests/external/csswg-test/css-writing-modes-3/line-box-direction-vrl-019.xht
@@ -27,7 +27,7 @@ height: 7em; /* Each line box has an inline-size of 7em */ list-style: none outside none; margin: 0em; - padding-top: 0em; /* overriding default -webkit-padding-start: 40px in several browsers */ + padding-top: 0em; /* overriding default padding-start: 40px in several browsers */ writing-mode: vertical-rl; } ]]></style>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/external-script-windows1250.js b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/external-script-windows1250.js deleted file mode 100644 index 4ac83bf..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/external-script-windows1250.js +++ /dev/null
@@ -1,5 +0,0 @@ -(function() { - window.getSomeString = function() { - return "œ湿Ÿ"; //<- these are five Polish letters, similar to scazz. It can be read correctly only with windows 1250 encoding. - }; -})();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-01-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-01-expected.txt deleted file mode 100644 index 1a29dad..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-01-expected.txt +++ /dev/null
@@ -1,9 +0,0 @@ -This is a testharness.js-based test. -PASS Script @type: unknown parameters -FAIL Script @type: unknown parameters 1 assert_equals: expected "śćążź" but got "ϾÂą¿Ÿ" -PASS Script @type: unknown parameters 2 -FAIL Script @type: unknown parameters 3 assert_equals: expected "śćążź" but got "œ湿Ÿ" -PASS Script @type: unknown parameters 4 -PASS Script @type: unknown parameters 5 -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-01.html b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-01.html deleted file mode 100644 index c5ac0d0..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-01.html +++ /dev/null
@@ -1,89 +0,0 @@ -<!DOCTYPE html> -<head> - <meta charset="utf-8"> - <title>Script @type: unknown parameters</title> - <link rel="author" title="askalski" href="github.com/askalski"> - <link rel="help" href="https://html.spec.whatwg.org/multipage/#scriptingLanguages"> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> - <div id="log"></div> - - <!-- "Step1" tests --> - <!-- charset is set incorrectly via Content Type "text/javascript;charset=utf-8" in response - which has priority before a correct setting in "charset" attribute of script tag. - --> - <script type="text/javascript" - src="serve-with-content-type.py?fn=external-script-windows1250.js&ct=text/javascript%3Bcharset=utf-8" charset="windows-1250"> - </script> - <script> - test(function() { - //these strings should not match, since the file charset is set incorrectly - assert_not_equals(window.getSomeString(), "śćążź"); - }); - </script> - <!-- charset is set correctly via Content Type "text/javascript;charset=utf-8" in response - which has priority before a incorrect setting in "charset" attribute of script tag. - --> - - <script type="text/javascript" - src="serve-with-content-type.py?fn=external-script-windows1250.js&ct=text/javascript%3Bcharset=windows-1250" charset="utf-8"> - </script> - <script> - //the charset is set correctly via Content Type "text/javascript;charset=windows-1250" in respones - test(function() { - assert_equals(window.getSomeString(), "śćążź"); - }); - </script> - - <!-- end of step1 tests, now step2 tests --> - <!-- in this case, the response's Content Type does not bring charset information. - Second step takes block character encoding if available.--> - <script type="text/javascript" - src="serve-with-content-type.py?fn=external-script-windows1250.js&ct=text/javascript" charset="utf-8"> - </script> - <script> - test(function() { - //these strings should not match, since the file charset is set incorrectly in "charset" tag of <script> above - assert_not_equals(window.getSomeString(), "śćążź"); - }); - </script> - <!-- charset is set correctly via Content Type "text/javascript;charset=utf-8" in response - which has priority before a incorrect setting in "charset" attribute of script tag. - --> - - <script type="text/javascript" - src="serve-with-content-type.py?fn=external-script-windows1250.js&ct=text/javascript" charset="windows-1250"> - </script> - <script> - //the charset is set correctly via content attribute in <script> above - test(function() { - assert_equals(window.getSomeString(), "śćążź"); - }); - </script> - - <!-- end of step2 tests, now step3 tests --> - <!-- in this case, neither response's Content Type nor charset attribute bring correct charset information. - Third step takes this document's character encoding (declared correctly as UTF-8).--> - <script type="text/javascript" - src="serve-with-content-type.py?fn=external-script-windows1250.js&ct=text/javascript"> - </script> - <script> - test(function() { - //these strings should not match, since the tested file is in windows-1250, and document is utf-8 - assert_not_equals(window.getSomeString(), "śćążź"); - }); - </script> - - <script type="text/javascript" - src="serve-with-content-type.py?fn=external-script-utf8.js&ct=text/javascript"> - </script> - <script> - //these strings should match, both document and tested file are utf-8 - test(function() { - assert_equals(window.getSomeString(), "śćążź"); - }); - </script> - - <!-- the last portion of tests (step4) are in file script-charset-02.html - -</head>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-02-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-02-expected.txt deleted file mode 100644 index 409c18225..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-02-expected.txt +++ /dev/null
@@ -1,5 +0,0 @@ -This is a testharness.js-based test. -PASS Script @type: unknown parameters -FAIL Script @type: unknown parameters 1 assert_equals: expected 5 but got 10 -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-02.html b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-02.html deleted file mode 100644 index 77a015b..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-02.html +++ /dev/null
@@ -1,40 +0,0 @@ -<!DOCTYPE html> -<head> - <!-- TODO: - askalski: while this test pass, it does not test anything now. - It should test, whether with no document.charset set in any way, the - external scripts will get decoded using utf-8 as fallback character encoding. - It seems like utf-8 is also a fallback encoding to html (my guess), so - the part of the code I was attempting to test is never reached. - --> - <title>Script @type: unknown parameters</title> - <link rel="author" title="askalski" href="github.com/askalski"> - <link rel="help" href="https://html.spec.whatwg.org/multipage/#scriptingLanguages"> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> - <div id="log"></div> - - <!-- test of step4, which is taking utf-8 as fallback --> - <!-- in this case, neither response's Content Type nor charset attribute bring correct charset information. - Furthermore, document's encoding is not set.--> - <script type="text/javascript" - src="serve-with-content-type.py?fn=external-script-windows1250.js&ct=text/javascript"> - </script> - <script> - test(function() { - //these strings should not match, since the tested file is in windows-1250, and fallback is defined as utf-8 - assert_not_equals(window.getSomeString().length, 5); - }); - </script> - - <script type="text/javascript" - src="serve-with-content-type.py?fn=external-script-utf8.js&ct=text/javascript"> - </script> - <script> - //these strings should match, since fallback utf-8 is the correct setting. - test(function() { - assert_equals(window.getSomeString().length, 5); - }); - </script> - -</head>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-03-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-03-expected.txt deleted file mode 100644 index db53b8b4..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-03-expected.txt +++ /dev/null
@@ -1,4 +0,0 @@ -This is a testharness.js-based test. -FAIL Script changing @charset assert_equals: expected "śćążź" but got "ϾÂą¿Ÿ" -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-03.html b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-03.html deleted file mode 100644 index 4ff4cc6..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/script-charset-03.html +++ /dev/null
@@ -1,20 +0,0 @@ -<!DOCTYPE html> -<head> -<meta charset="utf-8"> -<title>Script changing @charset</title> -<link rel="help" href="https://html.spec.whatwg.org/multipage/#scriptingLanguages"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<div id="log"></div> -<script> -async_test(function() { - var s = document.createElement("script"); - s.src = "external-script-windows1250.js"; - s.charset = "windows-1250"; - document.body.appendChild(s); - s.charset = "utf-8"; - window.onload = this.step_func_done(function() { - assert_equals(window.getSomeString(), "\u015b\u0107\u0105\u017c\u017a"); - }); -}) -</script>
diff --git a/third_party/WebKit/LayoutTests/fast/scrolling/scrollable-area-frame-inherited-visibility-hidden-expected.txt b/third_party/WebKit/LayoutTests/fast/scrolling/scrollable-area-frame-inherited-visibility-hidden-expected.txt deleted file mode 100644 index a7e32d90..0000000 --- a/third_party/WebKit/LayoutTests/fast/scrolling/scrollable-area-frame-inherited-visibility-hidden-expected.txt +++ /dev/null
@@ -1,5 +0,0 @@ - PASS successfullyParsed is true - -TEST COMPLETE -PASS true is true -
diff --git a/third_party/WebKit/LayoutTests/fast/scrolling/scrollable-area-frame-inherited-visibility-hidden.html b/third_party/WebKit/LayoutTests/fast/scrolling/scrollable-area-frame-inherited-visibility-hidden.html deleted file mode 100644 index 0eeb7f47..0000000 --- a/third_party/WebKit/LayoutTests/fast/scrolling/scrollable-area-frame-inherited-visibility-hidden.html +++ /dev/null
@@ -1,12 +0,0 @@ -<!DOCTYPE html> -<head> - <style type="text/css"> @import "resources/scrollable-style.css"; </style> - <script src="../../resources/js-test.js"></script> - <script src="resources/scrollable-area.js"></script> -</head> -<body onload="runTest(0);"> - <span style="visibility: hidden"> - <iframe width=120 scrolling=yes src="resources/generic-scrollable-content.html"></iframe> - </span> - <div id='console'></div> -</html>
diff --git a/third_party/WebKit/LayoutTests/fast/scrolling/scrollable-area-frame-scrolling-yes-display-none-expected.txt b/third_party/WebKit/LayoutTests/fast/scrolling/scrollable-area-frame-scrolling-yes-display-none-expected.txt deleted file mode 100644 index 8d3073cdc..0000000 --- a/third_party/WebKit/LayoutTests/fast/scrolling/scrollable-area-frame-scrolling-yes-display-none-expected.txt +++ /dev/null
@@ -1,5 +0,0 @@ -PASS successfullyParsed is true - -TEST COMPLETE -PASS true is true -
diff --git a/third_party/WebKit/LayoutTests/fast/scrolling/scrollable-area-frame-scrolling-yes-display-none.html b/third_party/WebKit/LayoutTests/fast/scrolling/scrollable-area-frame-scrolling-yes-display-none.html deleted file mode 100644 index 2d2ce3265..0000000 --- a/third_party/WebKit/LayoutTests/fast/scrolling/scrollable-area-frame-scrolling-yes-display-none.html +++ /dev/null
@@ -1,10 +0,0 @@ -<!DOCTYPE html> -<head> - <style type="text/css"> @import "resources/scrollable-style.css"; </style> - <script src="../../resources/js-test.js"></script> - <script src="resources/scrollable-area.js"></script> -</head> -<body onload="runTest(0);"> - <iframe width=120 scrolling=yes style="display: none;" src="resources/generic-scrollable-content.html"></iframe> - <div id='console'></div> -</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-aria-1.1-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-aria-1.1-expected.txt new file mode 100644 index 0000000..2678ec3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-aria-1.1-expected.txt
@@ -0,0 +1,261 @@ + +This text field has an error! + + +Details 1 +Details 2 +Select All +{ + "nodeId": "<string>", + "ignored": false, + "role": { + "type": "role", + "value": "textbox" + }, + "name": { + "type": "computedString", + "value": "", + "sources": [ + { + "type": "relatedElement", + "attribute": "aria-labelledby" + }, + { + "type": "attribute", + "attribute": "aria-label" + }, + { + "type": "relatedElement", + "nativeSource": "label" + }, + { + "type": "placeholder", + "attribute": "placeholder" + }, + { + "type": "placeholder", + "attribute": "aria-placeholder" + }, + { + "type": "attribute", + "attribute": "title" + } + ] + }, + "properties": [ + { + "name": "invalid", + "value": { + "type": "token", + "value": "false" + } + }, + { + "name": "multiline", + "value": { + "type": "boolean", + "value": false + } + }, + { + "name": "readonly", + "value": { + "type": "boolean", + "value": false + } + }, + { + "name": "required", + "value": { + "type": "boolean", + "value": false + } + }, + { + "name": "errormessage", + "value": { + "type": "idref", + "relatedNodes": [ + { + "idref": "err", + "nodeResult": "h3#err" + } + ] + } + } + ], + "domNode": "input" +} +{ + "nodeId": "<string>", + "ignored": false, + "role": { + "type": "role", + "value": "img" + }, + "name": { + "type": "computedString", + "value": "Label", + "sources": [ + { + "type": "relatedElement", + "attribute": "aria-labelledby" + }, + { + "type": "attribute", + "value": { + "type": "computedString", + "value": "Label" + }, + "attribute": "aria-label", + "attributeValue": { + "type": "string", + "value": "Label" + } + }, + { + "type": "attribute", + "attribute": "alt", + "superseded": true + }, + { + "type": "attribute", + "attribute": "title", + "superseded": true + } + ] + }, + "properties": [ + { + "name": "details", + "value": { + "type": "idrefList", + "value": "", + "relatedNodes": [ + { + "idref": "d1", + "nodeResult": "div#d1" + }, + { + "idref": "d2", + "nodeResult": "div#d2" + } + ] + } + } + ], + "domNode": "img" +} +{ + "nodeId": "<string>", + "ignored": false, + "role": { + "type": "role", + "value": "button" + }, + "name": { + "type": "computedString", + "value": "Select All", + "sources": [ + { + "type": "relatedElement", + "attribute": "aria-labelledby" + }, + { + "type": "attribute", + "attribute": "aria-label" + }, + { + "type": "relatedElement", + "nativeSource": "label" + }, + { + "type": "contents", + "value": { + "type": "computedString", + "value": "Select All" + } + }, + { + "type": "attribute", + "attribute": "title", + "superseded": true + } + ] + }, + "properties": [ + { + "name": "invalid", + "value": { + "type": "token", + "value": "false" + } + }, + { + "name": "keyshortcuts", + "value": { + "type": "string", + "value": "Ctrl+A" + } + } + ], + "domNode": "button" +} +{ + "nodeId": "<string>", + "ignored": false, + "role": { + "type": "role", + "value": "checkbox" + }, + "name": { + "type": "computedString", + "value": "", + "sources": [ + { + "type": "relatedElement", + "attribute": "aria-labelledby" + }, + { + "type": "attribute", + "attribute": "aria-label" + }, + { + "type": "relatedElement", + "nativeSource": "label" + }, + { + "type": "contents" + }, + { + "type": "attribute", + "attribute": "title" + } + ] + }, + "properties": [ + { + "name": "invalid", + "value": { + "type": "token", + "value": "false" + } + }, + { + "name": "checked", + "value": { + "type": "tristate", + "value": "true" + } + }, + { + "name": "roledescription", + "value": { + "type": "string", + "value": "Lightswitch" + } + } + ], + "domNode": "input" +} +
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-aria-1.1.html b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-aria-1.1.html new file mode 100644 index 0000000..3384f77f --- /dev/null +++ b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-aria-1.1.html
@@ -0,0 +1,30 @@ +<html> +<head> +<script type="text/javascript" src="../../http/tests/inspector-protocol/dom-protocol-test.js"></script> +<script type="text/javascript" src="../../http/tests/inspector-protocol/inspector-protocol-test.js"></script> +<script type="text/javascript" src="./accessibility-dumpAccessibilityNodes.js"></script> +<script> + +function test() +{ + InspectorTest.sendCommand("DOM.getDocument", {}, (msg) => { + InspectorTest.dumpAccessibilityNodesBySelectorAndCompleteTest("[data-dump]", false, msg); + }); +} + +</script> +</head> +<body onLoad="runTest();"> + <input data-dump aria-errormessage="err"> + <h3 id="err">This text field has an error!</h3> + + <img data-dump aria-details="d1 d2" aria-label="Label"> + <div id="d1">Details 1</div> + <div id="d2">Details 2</div> + + <button data-dump aria-keyshortcuts="Ctrl+A">Select All</button> + + <input data-dump type="checkbox" aria-roledescription="Lightswitch" checked> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/inspector/storage-panel-dom-storage-update.html b/third_party/WebKit/LayoutTests/inspector/storage-panel-dom-storage-update.html index 139a597f..7db2cf2 100644 --- a/third_party/WebKit/LayoutTests/inspector/storage-panel-dom-storage-update.html +++ b/third_party/WebKit/LayoutTests/inspector/storage-panel-dom-storage-update.html
@@ -68,7 +68,7 @@ UI.panels.resources._showDOMStorage(storage); view = UI.panels.resources._domStorageViews.get(storage); - InspectorTest.addSniffer(view, "_dataGridForDOMStorageItems", viewUpdated); + InspectorTest.addSniffer(view, "_showDOMStorageItems", viewUpdated); }, function addItemTest(next)
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/edge-inclusive-intersection.html b/third_party/WebKit/LayoutTests/intersection-observer/edge-inclusive-intersection.html index 6d5ced2..47d9933 100644 --- a/third_party/WebKit/LayoutTests/intersection-observer/edge-inclusive-intersection.html +++ b/third_party/WebKit/LayoutTests/intersection-observer/edge-inclusive-intersection.html
@@ -40,30 +40,30 @@ observer.observe(target); entries = entries.concat(observer.takeRecords()); assert_equals(entries.length, 0, "No initial notifications."); - runTestCycle(step0, "No notifications after first rAF."); + runTestCycle(step0, "First rAF."); }, "IntersectionObserver should detect and report edge-adjacent and zero-area intersections."); function step0() { runTestCycle(step1, "Set transform=translateY(200px) on target."); - assert_equals(entries.length, 0, "entries.length"); + checkLastEntry(entries, 0, [8, 108, 258, 358, 0, 0, 0, 0, 8, 208, 8, 208, target]); target.style.transform = "translateY(200px)"; } function step1() { runTestCycle(step2, "Set transform=translateY(201px) on target."); - checkLastEntry(entries, 0, [8, 108, 208, 308, 8, 108, 208, 208, 8, 208, 8, 208, target]); + checkLastEntry(entries, 1, [8, 108, 208, 308, 8, 108, 208, 208, 8, 208, 8, 208, target]); target.style.transform = "translateY(201px)"; } function step2() { runTestCycle(step3, "Set transform=translateY(185px) on target."); - checkLastEntry(entries, 1); + checkLastEntry(entries, 2); target.style.height = "0px"; target.style.width = "300px"; target.style.transform = "translateY(185px)"; } function step3() { - checkLastEntry(entries, 2, [8, 308, 193, 193, 8, 208, 193, 193, 8, 208, 8, 208, target]); + checkLastEntry(entries, 3, [8, 308, 193, 193, 8, 208, 193, 193, 8, 208, 8, 208, target]); } </script>
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html b/third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html index 4d42e27..c4cb0e9 100644 --- a/third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html +++ b/third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html
@@ -41,30 +41,30 @@ observer.observe(target); entries = entries.concat(observer.takeRecords()); assert_equals(entries.length, 0, "No initial notifications."); - runTestCycle(step0, "No notifications after first rAF."); + runTestCycle(step0, "First rAF."); }, "Observer with the implicit root; target in a same-origin iframe."); }; function step0() { document.scrollingElement.scrollTop = 200; runTestCycle(step1, "document.scrollingElement.scrollTop = 200"); - assert_equals(entries.length, 0, "entries.length == 0"); + checkLastEntry(entries, 0, [8, 108, 208, 308, 0, 0, 0, 0, 0, 785, 0, 600, target]); } function step1() { iframe.contentDocument.scrollingElement.scrollTop = 250; runTestCycle(step2, "iframe.contentDocument.scrollingElement.scrollTop = 250"); - assert_equals(entries.length, 0, "entries.length == 0"); + assert_equals(entries.length, 1, "entries.length == 1"); } function step2() { document.scrollingElement.scrollTop = 100; runTestCycle(step3, "document.scrollingElement.scrollTop = 100"); - checkLastEntry(entries, 0, [8, 108, -42, 58, 8, 108, 0, 58, 0, 785, 0, 600, target]); + checkLastEntry(entries, 1, [8, 108, -42, 58, 8, 108, 0, 58, 0, 785, 0, 600, target]); } function step3() { - checkLastEntry(entries, 1, [8, 108, -42, 58, 0, 0, 0, 0, 0, 785, 0, 600, target]); + checkLastEntry(entries, 2, [8, 108, -42, 58, 0, 0, 0, 0, 0, 785, 0, 600, target]); document.scrollingElement.scrollTop = 0; } </script>
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/multiple-targets.html b/third_party/WebKit/LayoutTests/intersection-observer/multiple-targets.html index 6c75d6e..06c52ad 100644 --- a/third_party/WebKit/LayoutTests/intersection-observer/multiple-targets.html +++ b/third_party/WebKit/LayoutTests/intersection-observer/multiple-targets.html
@@ -47,34 +47,37 @@ observer.observe(target3); entries = entries.concat(observer.takeRecords()); assert_equals(entries.length, 0, "No initial notifications."); - runTestCycle(step0, "No notifications after first rAF."); + runTestCycle(step0, "First rAF."); }, "One observer with multiple targets."); function step0() { document.scrollingElement.scrollTop = 150; runTestCycle(step1, "document.scrollingElement.scrollTop = 150"); - assert_equals(entries.length, 0, "No notifications."); + assert_equals(entries.length, 3, "Three initial notifications."); + assert_equals(entries[0].target, target1, "entries[0].target === target1"); + assert_equals(entries[1].target, target2, "entries[1].target === target2"); + assert_equals(entries[2].target, target3, "entries[2].target === target3"); } function step1() { document.scrollingElement.scrollTop = 10000; runTestCycle(step2, "document.scrollingElement.scrollTop = 10000"); - assert_equals(entries.length, 1, "One notification."); - assert_equals(entries[0].target, target1, "entries[0].target === target1"); + assert_equals(entries.length, 4, "Four notifications."); + assert_equals(entries[3].target, target1, "entries[3].target === target1"); } function step2() { document.scrollingElement.scrollTop = 0; runTestCycle(step3, "document.scrollingElement.scrollTop = 0"); - assert_equals(entries.length, 3, "Three notifications."); - assert_equals(entries[1].target, target2, "entries[1].target === target2"); - assert_equals(entries[2].target, target3, "entries[2].target === target3"); + assert_equals(entries.length, 6, "Six notifications."); + assert_equals(entries[4].target, target2, "entries[4].target === target2"); + assert_equals(entries[5].target, target3, "entries[5].target === target3"); } function step3() { - assert_equals(entries.length, 6, "Six notifications."); - assert_equals(entries[3].target, target1, "entries[3].target === target1"); - assert_equals(entries[4].target, target2, "entries[4].target === target2"); - assert_equals(entries[5].target, target3, "entries[5].target === target3"); + assert_equals(entries.length, 9, "Nine notifications."); + assert_equals(entries[6].target, target1, "entries[6].target === target1"); + assert_equals(entries[7].target, target2, "entries[7].target === target2"); + assert_equals(entries[8].target, target3, "entries[8].target === target3"); } </script>
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/multiple-thresholds.html b/third_party/WebKit/LayoutTests/intersection-observer/multiple-thresholds.html index b4931967..03ea9bd 100644 --- a/third_party/WebKit/LayoutTests/intersection-observer/multiple-thresholds.html +++ b/third_party/WebKit/LayoutTests/intersection-observer/multiple-thresholds.html
@@ -38,59 +38,59 @@ observer.observe(target); entries = entries.concat(observer.takeRecords()); assert_equals(entries.length, 0, "No initial notifications."); - runTestCycle(step0, "No notifications after first rAF."); + runTestCycle(step0, "First rAF."); }, "Observer with multiple thresholds."); function step0() { document.scrollingElement.scrollTop = 120; runTestCycle(step1, "document.scrollingElement.scrollTop = 120"); - assert_equals(entries.length, 0, "No notifications after first rAF."); + checkLastEntry(entries, 0, [8, 108, 708, 808, 0, 0, 0, 0, 0, 785, 0, 600, target]); } function step1() { document.scrollingElement.scrollTop = 160; runTestCycle(step2, "document.scrollingElement.scrollTop = 160"); - checkLastEntry(entries, 0, [8, 108, 588, 688, 8, 108, 588, 600, 0, 785, 0, 600, target]); + checkLastEntry(entries, 1, [8, 108, 588, 688, 8, 108, 588, 600, 0, 785, 0, 600, target]); } function step2() { document.scrollingElement.scrollTop = 200; runTestCycle(step3, "document.scrollingElement.scrollTop = 200"); - checkLastEntry(entries, 1, [8, 108, 548, 648, 8, 108, 548, 600, 0, 785, 0, 600, target]); + checkLastEntry(entries, 2, [8, 108, 548, 648, 8, 108, 548, 600, 0, 785, 0, 600, target]); } function step3() { document.scrollingElement.scrollTop = 240; runTestCycle(step4, "document.scrollingElement.scrollTop = 240"); - checkLastEntry(entries, 2, [8, 108, 508, 608, 8, 108, 508, 600, 0, 785, 0, 600, target]); + checkLastEntry(entries, 3, [8, 108, 508, 608, 8, 108, 508, 600, 0, 785, 0, 600, target]); } function step4() { document.scrollingElement.scrollTop = 740; runTestCycle(step5, "document.scrollingElement.scrollTop = 740"); - checkLastEntry(entries, 3, [8, 108, 468, 568, 8, 108, 468, 568, 0, 785, 0, 600, target]); + checkLastEntry(entries, 4, [8, 108, 468, 568, 8, 108, 468, 568, 0, 785, 0, 600, target]); } function step5() { document.scrollingElement.scrollTop = 760; runTestCycle(step6, "document.scrollingElement.scrollTop = 760"); - checkLastEntry(entries, 4, [8, 108, -32, 68, 8, 108, 0, 68, 0, 785, 0, 600, target]); + checkLastEntry(entries, 5, [8, 108, -32, 68, 8, 108, 0, 68, 0, 785, 0, 600, target]); } function step6() { document.scrollingElement.scrollTop = 800; runTestCycle(step7, "document.scrollingElement.scrollTop = 800"); - checkLastEntry(entries, 5, [8, 108, -52, 48, 8, 108, 0, 48, 0, 785, 0, 600, target]); + checkLastEntry(entries, 6, [8, 108, -52, 48, 8, 108, 0, 48, 0, 785, 0, 600, target]); } function step7() { - checkLastEntry(entries, 6, [8, 108, -92, 8, 8, 108, 0, 8, 0, 785, 0, 600, target]); + checkLastEntry(entries, 7, [8, 108, -92, 8, 8, 108, 0, 8, 0, 785, 0, 600, target]); document.scrollingElement.scrollTop = 820; runTestCycle(step8, "document.scrollingElement.scrollTop = 820"); } function step8() { - checkLastEntry(entries, 7, [8, 108, -112, -12, 0, 0, 0, 0, 0, 785, 0, 600, target]); + checkLastEntry(entries, 8, [8, 108, -112, -12, 0, 0, 0, 0, 0, 785, 0, 600, target]); document.scrollingElement.scrollTop = 0; } </script>
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/observer-without-js-reference.html b/third_party/WebKit/LayoutTests/intersection-observer/observer-without-js-reference.html index 1cdc084..6525b111 100644 --- a/third_party/WebKit/LayoutTests/intersection-observer/observer-without-js-reference.html +++ b/third_party/WebKit/LayoutTests/intersection-observer/observer-without-js-reference.html
@@ -43,11 +43,11 @@ function step0() { document.scrollingElement.scrollTop = 300; runTestCycle(step1, "document.scrollingElement.scrollTop = 300"); - assert_equals(entries.length, 0, "No notifications after first rAF."); + assert_equals(entries.length, 1, "One notification."); } function step1() { document.scrollingElement.scrollTop = 0; - assert_equals(entries.length, 1, "One notification."); + assert_equals(entries.length, 2, "Two notifications."); } </script>
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/remove-element.html b/third_party/WebKit/LayoutTests/intersection-observer/remove-element.html index dcb3ff8..f60aa23 100644 --- a/third_party/WebKit/LayoutTests/intersection-observer/remove-element.html +++ b/third_party/WebKit/LayoutTests/intersection-observer/remove-element.html
@@ -57,29 +57,29 @@ function step0() { root.scrollTop = 150; runTestCycle(step1, "root.scrollTop = 150"); - assert_equals(entries.length, 0, "No notifications after first rAF."); + checkLastEntry(entries, 0, [11, 111, 311, 411, 0, 0, 0, 0, 11, 111, 11, 211, target]); } function step1() { root.removeChild(target); runTestCycle(step2, "root.removeChild(target)."); - checkLastEntry(entries, 0, [11, 111, 161, 261, 11, 111, 161, 211, 11, 111, 11, 211, target]); + checkLastEntry(entries, 1, [11, 111, 161, 261, 11, 111, 161, 211, 11, 111, 11, 211, target]); } function step2() { root.scrollTop = 0; root.insertBefore(target, trailingSpace); runTestCycle(step3, "root.insertBefore(target, trailingSpace)."); - checkLastEntry(entries, 1, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, target]); + checkLastEntry(entries, 2, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, target]); } function step3() { root.scrollTop = 150; runTestCycle(step4, "root.scrollTop = 150 after reinserting target."); - checkLastEntry(entries, 1); + checkLastEntry(entries, 2); } function step4() { - checkLastEntry(entries, 2, [11, 111, 161, 261, 11, 111, 161, 211, 11, 111, 11, 211, target]); + checkLastEntry(entries, 3, [11, 111, 161, 261, 11, 111, 161, 211, 11, 111, 11, 211, target]); } </script>
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/resources/cross-origin-subframe.html b/third_party/WebKit/LayoutTests/intersection-observer/resources/cross-origin-subframe.html index 7416834..0cc117cb 100644 --- a/third_party/WebKit/LayoutTests/intersection-observer/resources/cross-origin-subframe.html +++ b/third_party/WebKit/LayoutTests/intersection-observer/resources/cross-origin-subframe.html
@@ -52,9 +52,15 @@ function step0() { entries = entries.concat(observer.takeRecords()); nextStep = step1; + var expected = [{ + boundingClientRect: coordinatesToClientRectJson(8, 208, 108, 308), + intersectionRect: coordinatesToClientRectJson(0, 0, 0, 0), + rootBounds: "null", + target: target.id + }]; port.postMessage({ actual: entries.map(entryToJson), - expected: [], + expected: expected, description: "First rAF" }, "*"); entries = [];
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/resources/observer-in-iframe-subframe.html b/third_party/WebKit/LayoutTests/intersection-observer/resources/observer-in-iframe-subframe.html index 711a9ce..ab19066 100644 --- a/third_party/WebKit/LayoutTests/intersection-observer/resources/observer-in-iframe-subframe.html +++ b/third_party/WebKit/LayoutTests/intersection-observer/resources/observer-in-iframe-subframe.html
@@ -56,10 +56,10 @@ function step1() { scroller.scrollTop = 250; runTestCycle(step2, "scroller.scrollTop = 250"); - assert_equals(entries.length, 0, "No notifications after first rAF."); + checkLastEntry(entries, 0, [8, 108, 308, 408, 0, 0, 0, 0, 8, 208, 8, 208, target]); } function step2() { - checkLastEntry(entries, 0, [8, 108, 58, 158, 8, 108, 58, 158, 8, 208, 8, 208, target]); + checkLastEntry(entries, 1, [8, 108, 58, 158, 8, 108, 58, 158, 8, 208, 8, 208, target]); } </script>
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/root-margin.html b/third_party/WebKit/LayoutTests/intersection-observer/root-margin.html index 0c2bc9f..6ccd802 100644 --- a/third_party/WebKit/LayoutTests/intersection-observer/root-margin.html +++ b/third_party/WebKit/LayoutTests/intersection-observer/root-margin.html
@@ -54,24 +54,24 @@ function step0() { document.scrollingElement.scrollLeft = 100; runTestCycle(step1, "document.scrollingElement.scrollLeft = 100"); - assert_equals(entries.length, 0, "No notifications after first rAF."); + checkLastEntry(entries, 0, [1012, 1112, 708, 808, 0, 0, 0, 0, -30, 942, -10, 819, target]); } function step1() { document.scrollingElement.scrollTop = 800; runTestCycle(step2, "document.scrollingElement.scrollTop = 800"); - checkLastEntry(entries, 0, [912, 1012, 708, 808, 912, 942, 708, 808, -30, 942, -10, 819, target]); + checkLastEntry(entries, 1, [912, 1012, 708, 808, 912, 942, 708, 808, -30, 942, -10, 819, target]); } function step2() { document.scrollingElement.scrollTop = 900; runTestCycle(step3, "document.scrollingElement.scrollTop = 900"); - checkLastEntry(entries, 0); + checkLastEntry(entries, 1); } function step3() { document.scrollingElement.scrollLeft = 0; document.scrollingElement.scrollTop = 0; - checkLastEntry(entries, 1, [912, 1012, -192, -92, 0, 0, 0, 0, -30, 942, -10, 819, target]); + checkLastEntry(entries, 2, [912, 1012, -192, -92, 0, 0, 0, 0, -30, 942, -10, 819, target]); } </script>
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/same-document-no-root.html b/third_party/WebKit/LayoutTests/intersection-observer/same-document-no-root.html index 27464d8..7ca9413 100644 --- a/third_party/WebKit/LayoutTests/intersection-observer/same-document-no-root.html +++ b/third_party/WebKit/LayoutTests/intersection-observer/same-document-no-root.html
@@ -45,17 +45,17 @@ function step0() { document.scrollingElement.scrollTop = 300; runTestCycle(step1, "document.scrollingElement.scrollTop = 300"); - assert_equals(entries.length, 0, "No notifications after first rAF."); + checkLastEntry(entries, 0, [8, 108, 708, 808, 0, 0, 0, 0, 0, 785, 0, 600, target]); } function step1() { document.scrollingElement.scrollTop = 100; runTestCycle(step2, "document.scrollingElement.scrollTop = 100"); - checkLastEntry(entries, 0, [8, 108, 408, 508, 8, 108, 408, 508, 0, 785, 0, 600, target]); + checkLastEntry(entries, 1, [8, 108, 408, 508, 8, 108, 408, 508, 0, 785, 0, 600, target]); } function step2() { document.scrollingElement.scrollTop = 0; - checkLastEntry(entries, 1, [8, 108, 608, 708, 0, 0, 0, 0, 0, 785, 0, 600, target]); + checkLastEntry(entries, 2, [8, 108, 608, 708, 0, 0, 0, 0, 0, 785, 0, 600, target]); } </script>
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/same-document-root.html b/third_party/WebKit/LayoutTests/intersection-observer/same-document-root.html index 32c92b2a..303d58d 100644 --- a/third_party/WebKit/LayoutTests/intersection-observer/same-document-root.html +++ b/third_party/WebKit/LayoutTests/intersection-observer/same-document-root.html
@@ -56,35 +56,35 @@ function step0() { document.scrollingElement.scrollTop = 600; runTestCycle(step1, "document.scrollingElement.scrollTop = 600."); - assert_equals(entries.length, 0, "No notifications after first rAF."); + checkLastEntry(entries, 0, [11, 111, 1011, 1111, 0, 0, 0, 0, 11, 111, 711, 911, target]); } function step1() { root.scrollTop = 150; runTestCycle(step2, "root.scrollTop = 150 with root scrolled into view."); - assert_equals(entries.length, 0, "No notifications after scrolling frame."); + assert_equals(entries.length, 1, "No notifications after scrolling frame."); } function step2() { document.scrollingElement.scrollTop = 0; runTestCycle(step3, "document.scrollingElement.scrollTop = 0."); - checkLastEntry(entries, 0, [11, 111, 261, 361, 11, 111, 261, 311, 11, 111, 111, 311, target]); + checkLastEntry(entries, 1, [11, 111, 261, 361, 11, 111, 261, 311, 11, 111, 111, 311, target]); } function step3() { root.scrollTop = 0; runTestCycle(step4, "root.scrollTop = 0"); - checkLastEntry(entries, 0); + checkLastEntry(entries, 1); } function step4() { root.scrollTop = 150; runTestCycle(step5, "root.scrollTop = 150 with root scrolled out of view."); - checkLastEntry(entries, 1, [11, 111, 1011, 1111, 0, 0, 0, 0, 11, 111, 711, 911, target]); + checkLastEntry(entries, 2, [11, 111, 1011, 1111, 0, 0, 0, 0, 11, 111, 711, 911, target]); } // This tests that notifications are generated even when the root element is off screen. function step5() { - checkLastEntry(entries, 2, [11, 111, 861, 961, 11, 111, 861, 911, 11, 111, 711, 911, target]); + checkLastEntry(entries, 3, [11, 111, 861, 961, 11, 111, 861, 911, 11, 111, 711, 911, target]); } </script>
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/same-document-zero-size-target.html b/third_party/WebKit/LayoutTests/intersection-observer/same-document-zero-size-target.html index 47692a4..b416553 100644 --- a/third_party/WebKit/LayoutTests/intersection-observer/same-document-zero-size-target.html +++ b/third_party/WebKit/LayoutTests/intersection-observer/same-document-zero-size-target.html
@@ -45,17 +45,17 @@ function step0() { document.scrollingElement.scrollTop = 300; runTestCycle(step1, "document.scrollingElement.scrollTop = 300"); - assert_equals(entries.length, 0, "No notifications after first rAF."); + checkLastEntry(entries, 0, [8, 8, 708, 708, 0, 0, 0, 0, 0, 785, 0, 600, target]); } function step1() { document.scrollingElement.scrollTop = 100; runTestCycle(step2, "document.scrollingElement.scrollTop = 100"); - checkLastEntry(entries, 0, [8, 8, 408, 408, 8, 8, 408, 408, 0, 785, 0, 600, target]); + checkLastEntry(entries, 1, [8, 8, 408, 408, 8, 8, 408, 408, 0, 785, 0, 600, target]); } function step2() { document.scrollingElement.scrollTop = 0; - checkLastEntry(entries, 1, [8, 8, 608, 608, 0, 0, 0, 0, 0, 785, 0, 600, target]); + checkLastEntry(entries, 2, [8, 8, 608, 608, 0, 0, 0, 0, 0, 785, 0, 600, target]); } </script>
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/timestamp.html b/third_party/WebKit/LayoutTests/intersection-observer/timestamp.html index 6667f54..f88ce91 100644 --- a/third_party/WebKit/LayoutTests/intersection-observer/timestamp.html +++ b/third_party/WebKit/LayoutTests/intersection-observer/timestamp.html
@@ -67,8 +67,8 @@ topWindowTimeBeforeNotification = performance.now(); iframeWindowTimeBeforeNotification = targetIframe.contentWindow.performance.now(); runTestCycle(step2, "Generate notifications."); - assert_equals(topWindowEntries.length, 0, "No notifications to top window observer."); - assert_equals(iframeWindowEntries.length, 0, "No notifications to iframe observer."); + assert_equals(topWindowEntries.length, 1, "One notification to top window observer."); + assert_equals(iframeWindowEntries.length, 1, "One notification to iframe observer."); } function step2() { @@ -81,16 +81,16 @@ assert_greater_than(topWindowTimeBeforeNotification, iframeWindowTimeAfterNotification, "Time ranges for top and iframe windows are disjoint."); - assert_equals(topWindowEntries.length, 1, "Top window observer has one notification."); + assert_equals(topWindowEntries.length, 2, "Top window observer has two notifications."); assert_between_inclusive( - topWindowEntries[0].time, + topWindowEntries[1].time, topWindowTimeBeforeNotification, topWindowTimeAfterNotification, "Notification to top window observer is within the expected range."); - assert_equals(iframeWindowEntries.length, 1, "Iframe observer has one notification."); + assert_equals(iframeWindowEntries.length, 2, "Iframe observer has two notifications."); assert_between_inclusive( - iframeWindowEntries[0].time, + iframeWindowEntries[1].time, iframeWindowTimeBeforeNotification, iframeWindowTimeAfterNotification, "Notification to iframe observer is within the expected range.");
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/unclipped-root.html b/third_party/WebKit/LayoutTests/intersection-observer/unclipped-root.html index 40d39ca4..490b9eb 100644 --- a/third_party/WebKit/LayoutTests/intersection-observer/unclipped-root.html +++ b/third_party/WebKit/LayoutTests/intersection-observer/unclipped-root.html
@@ -41,17 +41,17 @@ observer.observe(target); entries = entries.concat(observer.takeRecords()); assert_equals(entries.length, 0, "No initial notifications."); - runTestCycle(step0, "First rAF should not generate notifications."); + runTestCycle(step0, "First rAF."); }, "Test that border bounding box is used to calculate intersection with a non-scrolling root."); function step0() { target.style.transform = "translateY(195px)"; runTestCycle(step1, "target.style.transform = 'translateY(195px)'"); - assert_equals(entries.length, 0, "No notifications after first rAF."); + checkLastEntry(entries, 0, [15, 115, 315, 415, 0, 0, 0, 0, 8, 182, 8, 222, target]); } function step1() { target.style.transform = ""; - checkLastEntry(entries, 0, [15, 115, 210, 310, 15, 115, 210, 222, 8, 182, 8, 222, target]); + checkLastEntry(entries, 1, [15, 115, 210, 310, 15, 115, 210, 222, 8, 182, 8, 222, target]); } </script>
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/zero-area-element-hidden.html b/third_party/WebKit/LayoutTests/intersection-observer/zero-area-element-hidden.html index d1946d1..03d9efb 100644 --- a/third_party/WebKit/LayoutTests/intersection-observer/zero-area-element-hidden.html +++ b/third_party/WebKit/LayoutTests/intersection-observer/zero-area-element-hidden.html
@@ -34,10 +34,10 @@ observer.observe(target); entries = entries.concat(observer.takeRecords()); assert_equals(entries.length, 0, "No initial notifications."); - runTestCycle(step0, "First rAF should not generate a notification."); -}, "No intersecting observations should be sent for a zero-area hidden target."); + runTestCycle(step0, "First rAF."); +}, "A zero-area hidden target should not be intersecting."); function step0() { - assert_equals(entries.length, 0, "No notifications after first rAF."); + checkLastEntry(entries, 0, [8, 8, -1000, -1000, 0, 0, 0, 0, 0, 800, 0, 600, target]); } </script>
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt index 41f77be..27230dcf 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -148,8 +148,8 @@ getter maxValue getter minValue getter value + method cancelAndHoldAtTime method cancelScheduledValues - method cancelValuesAndHoldAtTime method constructor method exponentialRampToValueAtTime method linearRampToValueAtTime
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt index 3f37f45f..d0861b99 100644 --- a/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -148,8 +148,8 @@ getter maxValue getter minValue getter value + method cancelAndHoldAtTime method cancelScheduledValues - method cancelValuesAndHoldAtTime method constructor method exponentialRampToValueAtTime method linearRampToValueAtTime
diff --git a/third_party/WebKit/LayoutTests/scrollingcoordinator/non-fast-scrollable-region-nested.html b/third_party/WebKit/LayoutTests/scrollingcoordinator/non-fast-scrollable-region-nested.html index 81a003f..0fa0d9b 100644 --- a/third_party/WebKit/LayoutTests/scrollingcoordinator/non-fast-scrollable-region-nested.html +++ b/third_party/WebKit/LayoutTests/scrollingcoordinator/non-fast-scrollable-region-nested.html
@@ -24,7 +24,7 @@ 'iframe are correctly offset by the iframe location.'); onload = function() { - nonFastScrollableRects = internals.nonFastScrollableRects(document); + nonFastScrollableRects = sortRects(internals.nonFastScrollableRects(document)); shouldBe('nonFastScrollableRects.length', '3'); shouldBeEqualToString('rectToString(nonFastScrollableRects[0])', '[51, 102, 200, 200]'); shouldBeEqualToString('rectToString(nonFastScrollableRects[1])', '[51, 402, 211, 211]');
diff --git a/third_party/WebKit/LayoutTests/scrollingcoordinator/non-fast-scrollable-region-scaled-iframe.html b/third_party/WebKit/LayoutTests/scrollingcoordinator/non-fast-scrollable-region-scaled-iframe.html index b417a54..6950de1 100644 --- a/third_party/WebKit/LayoutTests/scrollingcoordinator/non-fast-scrollable-region-scaled-iframe.html +++ b/third_party/WebKit/LayoutTests/scrollingcoordinator/non-fast-scrollable-region-scaled-iframe.html
@@ -9,10 +9,14 @@ padding: 10px; border: none; } + .spacer { + height: 2000px; + } </style> <iframe src="data:text/html;charset=utf-8,<html><body style='width:1000px;height:1000px;'>Should be covered by a green overlay.</body></html>"></iframe> <div id="console"></div> +<div class="spacer"></div> <script src="../resources/js-test.js"></script> <script src="../resources/run-after-layout-and-paint.js"></script>
diff --git a/third_party/WebKit/LayoutTests/scrollingcoordinator/non-fast-scrollable-transform-changed.html b/third_party/WebKit/LayoutTests/scrollingcoordinator/non-fast-scrollable-transform-changed.html index 3b711c5d..a824b47 100644 --- a/third_party/WebKit/LayoutTests/scrollingcoordinator/non-fast-scrollable-transform-changed.html +++ b/third_party/WebKit/LayoutTests/scrollingcoordinator/non-fast-scrollable-transform-changed.html
@@ -18,12 +18,17 @@ width: 1000px; background: linear-gradient(to bottom, red, white); } + + .spacer { + height: 2000px; + } </style> <div id='nonFastRegion'><div>This should be covered by a green overlay.</div></div> <p>A single square should be visible covered by a green overlay.</p> <div id="console"></div> +<div class="spacer"></div> <script src="../resources/js-test.js"></script> <script src="../resources/run-after-layout-and-paint.js"></script> @@ -71,4 +76,4 @@ finishJSTest(); }, 0); } -</script> \ No newline at end of file +</script>
diff --git a/third_party/WebKit/LayoutTests/scrollingcoordinator/plugin-with-wheel-handler-expected.txt b/third_party/WebKit/LayoutTests/scrollingcoordinator/plugin-with-wheel-handler-expected.txt index bb74ab8f..72b26ae 100644 --- a/third_party/WebKit/LayoutTests/scrollingcoordinator/plugin-with-wheel-handler-expected.txt +++ b/third_party/WebKit/LayoutTests/scrollingcoordinator/plugin-with-wheel-handler-expected.txt
@@ -8,3 +8,4 @@ TEST COMPLETE +
diff --git a/third_party/WebKit/LayoutTests/scrollingcoordinator/plugin-with-wheel-handler.html b/third_party/WebKit/LayoutTests/scrollingcoordinator/plugin-with-wheel-handler.html index 1e6c9165..3f1208e 100644 --- a/third_party/WebKit/LayoutTests/scrollingcoordinator/plugin-with-wheel-handler.html +++ b/third_party/WebKit/LayoutTests/scrollingcoordinator/plugin-with-wheel-handler.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <script src="../resources/js-test.js"></script> +<script src="../resources/run-after-layout-and-paint.js"></script> <script> description('This test ensures that a plugin which wants to receive wheel ' + @@ -7,6 +8,7 @@ window.jsTestIsAsync = true; onload = function() { + runAfterLayoutAndPaint(function() { if (window.internals) { shouldBe('internals.nonFastScrollableRects(document).length', '1'); } else { @@ -14,7 +16,9 @@ } finishJSTest(); + }); } </script> <embed id="plugin" type="application/x-webkit-test-webplugin"></embed> +<div style="height:2000px"></div>
diff --git a/third_party/WebKit/LayoutTests/scrollingcoordinator/resources/non-fast-scrollable-region-testing.js b/third_party/WebKit/LayoutTests/scrollingcoordinator/resources/non-fast-scrollable-region-testing.js index 51500d2f..72a0d912 100644 --- a/third_party/WebKit/LayoutTests/scrollingcoordinator/resources/non-fast-scrollable-region-testing.js +++ b/third_party/WebKit/LayoutTests/scrollingcoordinator/resources/non-fast-scrollable-region-testing.js
@@ -34,3 +34,11 @@ return '[' + [rect.left, rect.top, rect.width, rect.height].join(', ') + ']'; } +function sortRects(rects) { + Array.prototype.sort.call(rects, (a, b) => ( + a.left - b.left || + a.top - b.top || + a.width - b.width || + a.height - b.height)); + return rects; +}
diff --git a/third_party/WebKit/LayoutTests/webaudio/AudioParam/audioparam-cancel-and-hold.html b/third_party/WebKit/LayoutTests/webaudio/AudioParam/audioparam-cancel-and-hold.html index 2e64ed08..05cb247 100644 --- a/third_party/WebKit/LayoutTests/webaudio/AudioParam/audioparam-cancel-and-hold.html +++ b/third_party/WebKit/LayoutTests/webaudio/AudioParam/audioparam-cancel-and-hold.html
@@ -209,9 +209,9 @@ }).then(task.done.bind(task)); }); - // Test automations scheduled after the call to cancelValuesAndHoldAtTime. + // Test automations scheduled after the call to cancelAndHoldAtTime. // Very similar to the above tests, but we also schedule an event after - // cancelValuesAndHoldAtTime and verify that curve after cancellation has + // cancelAndHoldAtTime and verify that curve after cancellation has // the correct values. audit.define("post cancel: Linear", function (task, should) { @@ -404,7 +404,7 @@ src.loop = true; // We'll do the automation tests with three gain nodes. One (g0) will - // have cancelValuesAndHoldAtTime and the other (g1) will not. g1 is + // have cancelAndHoldAtTime and the other (g1) will not. g1 is // used as the expected result for that automation up to the // cancellation point. They should be the same. The third node (g2) is // used for testing automations inserted after the cancellation point, @@ -428,7 +428,7 @@ // Cancel scheduled events somewhere in the middle of the test // automation. - g0.gain.cancelValuesAndHoldAtTime(cancelTime); + g0.gain.cancelAndHoldAtTime(cancelTime); let constantEndTime; if (postCancelTest) { @@ -438,8 +438,8 @@ } // Connect everything together (with a merger to make a two-channel - // result). Channel 0 is the test (with cancelValuesAndHoldAtTime) and - // channel 1 is the reference (without cancelValuesAndHoldAtTime). + // result). Channel 0 is the test (with cancelAndHoldAtTime) and + // channel 1 is the reference (without cancelAndHoldAtTime). // Channel 1 is used to verify that everything up to the cancellation // has the correct values. src.connect(g0);
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt index e9f4688..1e3472d 100644 --- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -213,8 +213,8 @@ getter maxValue getter minValue getter value + method cancelAndHoldAtTime method cancelScheduledValues - method cancelValuesAndHoldAtTime method constructor method exponentialRampToValueAtTime method linearRampToValueAtTime
diff --git a/third_party/WebKit/Source/.gitignore b/third_party/WebKit/Source/.gitignore deleted file mode 100644 index 94618e33..0000000 --- a/third_party/WebKit/Source/.gitignore +++ /dev/null
@@ -1,5 +0,0 @@ -# XCode projects generated on Mac. -*.xcodeproj - -# Goma. -core/gomacc.lock
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptPromisePropertyTest.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptPromisePropertyTest.cpp index 0ede386..4bee2dff 100644 --- a/third_party/WebKit/Source/bindings/core/v8/ScriptPromisePropertyTest.cpp +++ b/third_party/WebKit/Source/bindings/core/v8/ScriptPromisePropertyTest.cpp
@@ -23,7 +23,7 @@ #include <memory> #include <v8.h> -using namespace blink; +namespace blink { namespace { @@ -174,6 +174,51 @@ Persistent<GarbageCollectedHolder> m_holder; }; +// Tests that ScriptPromiseProperty works with a non ScriptWrappable resolution +// target. +class ScriptPromisePropertyNonScriptWrappableResolutionTargetTest + : public ScriptPromisePropertyTestBase, + public ::testing::Test { + public: + template <typename T> + void test(const T& value, + const char* expected, + const char* file, + size_t line) { + typedef ScriptPromiseProperty<Member<GarbageCollectedScriptWrappable>, T, + ToV8UndefinedGenerator> + Property; + Property* property = + new Property(&document(), new GarbageCollectedScriptWrappable("holder"), + Property::Ready); + size_t nResolveCalls = 0; + ScriptValue actualValue; + String actual; + { + ScriptState::Scope scope(mainScriptState()); + property->promise(DOMWrapperWorld::mainWorld()) + .then(stub(currentScriptState(), actualValue, nResolveCalls), + notReached(currentScriptState())); + } + property->resolve(value); + v8::MicrotasksScope::PerformCheckpoint(isolate()); + { + ScriptState::Scope scope(mainScriptState()); + actual = toCoreString(actualValue.v8Value() + ->ToString(mainScriptState()->context()) + .ToLocalChecked()); + } + if (expected != actual) { + ADD_FAILURE_AT(file, line) + << "toV8 returns an incorrect value.\n Actual: " + << actual.utf8().data() << "\nExpected: " << expected; + return; + } + } +}; + +} // namespace + TEST_F(ScriptPromisePropertyGarbageCollectedTest, Promise_IsStableObjectInMainWorld) { ScriptPromise v = getProperty()->promise(DOMWrapperWorld::mainWorld()); @@ -424,49 +469,6 @@ EXPECT_NE(oldActual, newActual); } -// Tests that ScriptPromiseProperty works with a non ScriptWrappable resolution -// target. -class ScriptPromisePropertyNonScriptWrappableResolutionTargetTest - : public ScriptPromisePropertyTestBase, - public ::testing::Test { - public: - template <typename T> - void test(const T& value, - const char* expected, - const char* file, - size_t line) { - typedef ScriptPromiseProperty<Member<GarbageCollectedScriptWrappable>, T, - ToV8UndefinedGenerator> - Property; - Property* property = - new Property(&document(), new GarbageCollectedScriptWrappable("holder"), - Property::Ready); - size_t nResolveCalls = 0; - ScriptValue actualValue; - String actual; - { - ScriptState::Scope scope(mainScriptState()); - property->promise(DOMWrapperWorld::mainWorld()) - .then(stub(currentScriptState(), actualValue, nResolveCalls), - notReached(currentScriptState())); - } - property->resolve(value); - v8::MicrotasksScope::PerformCheckpoint(isolate()); - { - ScriptState::Scope scope(mainScriptState()); - actual = toCoreString(actualValue.v8Value() - ->ToString(mainScriptState()->context()) - .ToLocalChecked()); - } - if (expected != actual) { - ADD_FAILURE_AT(file, line) - << "toV8 returns an incorrect value.\n Actual: " - << actual.utf8().data() << "\nExpected: " << expected; - return; - } - } -}; - TEST_F(ScriptPromisePropertyNonScriptWrappableResolutionTargetTest, ResolveWithUndefined) { test(ToV8UndefinedGenerator(), "undefined", __FILE__, __LINE__); @@ -482,4 +484,4 @@ test(-1, "-1", __FILE__, __LINE__); } -} // namespace +} // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValueFuzzer.cpp b/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValueFuzzer.cpp index 1c4bb39..a794acb 100644 --- a/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValueFuzzer.cpp +++ b/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValueFuzzer.cpp
@@ -20,7 +20,7 @@ #include <cstdint> #include <v8.h> -using namespace blink; +namespace blink { namespace { @@ -47,7 +47,7 @@ } // namespace -extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { +int LLVMFuzzerInitialize(int* argc, char*** argv) { const char kExposeGC[] = "--expose_gc"; v8::V8::SetFlagsFromString(kExposeGC, sizeof(kExposeGC)); InitializeBlinkFuzzTest(argc, argv); @@ -62,7 +62,7 @@ return 0; } -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // Odd sizes are handled in various ways, depending how they arrive. // Let's not worry about that case here. if (size % sizeof(UChar)) @@ -110,3 +110,13 @@ return 0; } + +} // namespace blink + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + return blink::LLVMFuzzerInitialize(argc, argv); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + return blink::LLVMFuzzerTestOneInput(data, size); +}
diff --git a/third_party/WebKit/Source/bindings/modules/v8/V8BindingForModulesTest.cpp b/third_party/WebKit/Source/bindings/modules/v8/V8BindingForModulesTest.cpp index 68df6fa..9c23c90 100644 --- a/third_party/WebKit/Source/bindings/modules/v8/V8BindingForModulesTest.cpp +++ b/third_party/WebKit/Source/bindings/modules/v8/V8BindingForModulesTest.cpp
@@ -34,7 +34,7 @@ #include "modules/indexeddb/IDBKeyPath.h" #include "testing/gtest/include/gtest/gtest.h" -using namespace blink; +namespace blink { namespace { @@ -132,6 +132,8 @@ checkKeyPathNullValue(isolate, scriptValue, "bar"); } +} // namespace + TEST(IDBKeyFromValueAndKeyPathTest, TopLevelPropertyNumberValue) { V8TestingScope scope; v8::Isolate* isolate = scope.isolate(); @@ -236,4 +238,4 @@ scriptObject, "foo.xyz.foo"); } -} // namespace +} // namespace blink
diff --git a/third_party/WebKit/Source/build/scripts/make_css_property_names.py b/third_party/WebKit/Source/build/scripts/make_css_property_names.py index b5f211d..ad1fbc02 100755 --- a/third_party/WebKit/Source/build/scripts/make_css_property_names.py +++ b/third_party/WebKit/Source/build/scripts/make_css_property_names.py
@@ -230,8 +230,16 @@ gperf_args = [self.gperf_path, '--key-positions=*', '-P', '-n'] gperf_args.extend(['-m', '50']) # Pick best of 50 attempts. gperf_args.append('-D') # Allow duplicate hashes -> More compact code. - gperf = subprocess.Popen(gperf_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True) - return gperf.communicate(gperf_input)[0] + + # If gperf isn't in the path we get an OSError. We don't want to use + # the normal solution of shell=True (as this has to run on many + # platforms), so instead we catch the error and raise a + # CalledProcessError like subprocess would do when shell=True is set. + try: + gperf = subprocess.Popen(gperf_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True) + return gperf.communicate(gperf_input)[0] + except OSError: + raise subprocess.CalledProcessError(127, gperf_args, output='Command not found.') if __name__ == "__main__":
diff --git a/third_party/WebKit/Source/core/css/CSSPaintValue.cpp b/third_party/WebKit/Source/core/css/CSSPaintValue.cpp index 70c9b3e9..d93ed24 100644 --- a/third_party/WebKit/Source/core/css/CSSPaintValue.cpp +++ b/third_party/WebKit/Source/core/css/CSSPaintValue.cpp
@@ -16,12 +16,22 @@ m_name(name), m_paintImageGeneratorObserver(new Observer(this)) {} +CSSPaintValue::CSSPaintValue(CSSCustomIdentValue* name, + Vector<RefPtr<CSSVariableData>>& variableData) + : CSSPaintValue(name) { + m_argumentVariableData.swap(variableData); +} + CSSPaintValue::~CSSPaintValue() {} String CSSPaintValue::customCSSText() const { StringBuilder result; result.append("paint("); result.append(m_name->customCSSText()); + for (const auto& variableData : m_argumentVariableData) { + result.append(", "); + result.append(variableData.get()->tokenRange().serialize()); + } result.append(')'); return result.toString(); } @@ -56,7 +66,7 @@ } bool CSSPaintValue::equals(const CSSPaintValue& other) const { - return name() == other.name(); + return name() == other.name() && customCSSText() == other.customCSSText(); } DEFINE_TRACE_AFTER_DISPATCH(CSSPaintValue) {
diff --git a/third_party/WebKit/Source/core/css/CSSPaintValue.h b/third_party/WebKit/Source/core/css/CSSPaintValue.h index 8c111e9..09aeece 100644 --- a/third_party/WebKit/Source/core/css/CSSPaintValue.h +++ b/third_party/WebKit/Source/core/css/CSSPaintValue.h
@@ -8,7 +8,9 @@ #include "core/css/CSSCustomIdentValue.h" #include "core/css/CSSImageGeneratorValue.h" #include "core/css/CSSPaintImageGenerator.h" +#include "core/css/CSSVariableData.h" #include "platform/heap/Handle.h" +#include "wtf/Vector.h" namespace blink { @@ -17,6 +19,12 @@ static CSSPaintValue* create(CSSCustomIdentValue* name) { return new CSSPaintValue(name); } + + static CSSPaintValue* create(CSSCustomIdentValue* name, + Vector<RefPtr<CSSVariableData>>& variableData) { + return new CSSPaintValue(name, variableData); + } + ~CSSPaintValue(); String customCSSText() const; @@ -46,6 +54,8 @@ private: explicit CSSPaintValue(CSSCustomIdentValue* name); + CSSPaintValue(CSSCustomIdentValue* name, Vector<RefPtr<CSSVariableData>>&); + class Observer final : public CSSPaintImageGenerator::Observer { WTF_MAKE_NONCOPYABLE(Observer); @@ -69,6 +79,7 @@ Member<CSSCustomIdentValue> m_name; Member<CSSPaintImageGenerator> m_generator; Member<Observer> m_paintImageGeneratorObserver; + Vector<RefPtr<CSSVariableData>> m_argumentVariableData; }; DEFINE_CSS_VALUE_TYPE_CASTS(CSSPaintValue, isPaintValue());
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParserHelpers.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParserHelpers.cpp index b356ef9..b691724 100644 --- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParserHelpers.cpp +++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParserHelpers.cpp
@@ -14,6 +14,7 @@ #include "core/css/CSSStringValue.h" #include "core/css/CSSURIValue.h" #include "core/css/CSSValuePair.h" +#include "core/css/CSSVariableData.h" #include "core/css/StyleColor.h" #include "core/css/parser/CSSParserContext.h" #include "core/frame/UseCounter.h" @@ -23,6 +24,25 @@ namespace CSSPropertyParserHelpers { +namespace { + +// Add CSSVariableData to variableData vector. +bool addCSSPaintArgument(const Vector<CSSParserToken>& tokens, + Vector<RefPtr<CSSVariableData>>* const variableData) { + CSSParserTokenRange tokenRange(tokens); + if (!tokenRange.atEnd()) { + RefPtr<CSSVariableData> unparsedCSSVariableData = + CSSVariableData::create(tokenRange, false, false); + if (unparsedCSSVariableData.get()) { + variableData->push_back(std::move(unparsedCSSVariableData)); + return true; + } + } + return false; +} + +} // namespace + void complete4Sides(CSSValue* side[4]) { if (side[3]) return; @@ -351,12 +371,17 @@ return consumeIdent(range); } +CSSCustomIdentValue* consumeCustomIdentWithToken(const CSSParserToken& token) { + if (token.type() != IdentToken || isCSSWideKeyword(token.value())) + return nullptr; + return CSSCustomIdentValue::create(token.value().toAtomicString()); +} + CSSCustomIdentValue* consumeCustomIdent(CSSParserTokenRange& range) { if (range.peek().type() != IdentToken || isCSSWideKeyword(range.peek().value())) return nullptr; - return CSSCustomIdentValue::create( - range.consumeIncludingWhitespace().value().toAtomicString()); + return consumeCustomIdentWithToken(range.consumeIncludingWhitespace()); } CSSStringValue* consumeString(CSSParserTokenRange& range) { @@ -1123,11 +1148,44 @@ const CSSParserContext* context) { DCHECK(RuntimeEnabledFeatures::cssPaintAPIEnabled()); - CSSCustomIdentValue* name = consumeCustomIdent(args); + const CSSParserToken& nameToken = args.consumeIncludingWhitespace(); + CSSCustomIdentValue* name = consumeCustomIdentWithToken(nameToken); if (!name) return nullptr; - return CSSPaintValue::create(name); + if (args.atEnd()) + return CSSPaintValue::create(name); + + if (!RuntimeEnabledFeatures::cssPaintAPIArgumentsEnabled()) { + // Arguments not enabled, but exists. Invalid. + return nullptr; + } + + // Begin parse paint arguments. + if (!consumeCommaIncludingWhitespace(args)) + return nullptr; + + // Consume arguments. Currently does not support complicated arguments + // like function calls. + // TODO(renjieliu): We may want to optimize the implementation by resolve + // variables early if paint function is registered. + Vector<CSSParserToken> argumentTokens; + Vector<RefPtr<CSSVariableData>> variableData; + while (!args.atEnd()) { + if (args.peek().type() != CommaToken) { + argumentTokens.push_back(args.consumeIncludingWhitespace()); + } else { + if (!addCSSPaintArgument(argumentTokens, &variableData)) + return nullptr; + argumentTokens.clear(); + if (!consumeCommaIncludingWhitespace(args)) + return nullptr; + } + } + if (!addCSSPaintArgument(argumentTokens, &variableData)) + return nullptr; + + return CSSPaintValue::create(name, variableData); } static CSSValue* consumeGeneratedImage(CSSParserTokenRange& range,
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParserTest.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParserTest.cpp index c5eb170b..5c636e95 100644 --- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParserTest.cpp +++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParserTest.cpp
@@ -20,6 +20,42 @@ return numberOfTracks; } +TEST(CSSPropertyParserTest, CSSPaint_NoArguments) { + const CSSValue* value = + CSSParser::parseSingleValue(CSSPropertyBackgroundImage, "paint(foo)"); + ASSERT_TRUE(value); + ASSERT_TRUE(value->isImageGeneratorValue()); + EXPECT_EQ(value->cssText(), "paint(foo)"); +} + +TEST(CSSPropertyParserTest, CSSPaint_ValidArguments) { + const CSSValue* value = CSSParser::parseSingleValue( + CSSPropertyBackgroundImage, "paint(bar, 10px, red)"); + ASSERT_TRUE(value); + ASSERT_TRUE(value->isImageGeneratorValue()); + EXPECT_EQ(value->cssText(), "paint(bar, 10px, red)"); +} + +TEST(CSSPropertyParserTest, CSSPaint_InvalidFormat) { + const CSSValue* value = + CSSParser::parseSingleValue(CSSPropertyBackgroundImage, "paint(foo bar)"); + // Illegal format should not be parsed. + ASSERT_FALSE(value); +} + +TEST(CSSPropertyParserTest, CSSPaint_TrailingComma) { + const CSSValue* value = CSSParser::parseSingleValue( + CSSPropertyBackgroundImage, "paint(bar, 10px, red,)"); + ASSERT_FALSE(value); +} + +TEST(CSSPropertyParserTest, CSSPaint_PaintArgumentsDiabled) { + RuntimeEnabledFeatures::setCSSPaintAPIArgumentsEnabled(false); + const CSSValue* value = CSSParser::parseSingleValue( + CSSPropertyBackgroundImage, "paint(bar, 10px, red)"); + ASSERT_FALSE(value); +} + TEST(CSSPropertyParserTest, GridTrackLimit1) { const CSSValue* value = CSSParser::parseSingleValue( CSSPropertyGridTemplateColumns, "repeat(999, 20px)");
diff --git a/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp b/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp index 5c12e1c..e2fdb57 100644 --- a/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp +++ b/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp
@@ -95,9 +95,9 @@ #include "platform/RuntimeEnabledFeatures.h" #include "wtf/StdLibExtras.h" -namespace { +namespace blink { -using namespace blink; +namespace { void setAnimationUpdateIfNeeded(StyleResolverState& state, Element& element) { // If any changes to CSS Animations were detected, stash the update away for @@ -141,8 +141,6 @@ } // namespace -namespace blink { - using namespace HTMLNames; ComputedStyle* StyleResolver::s_styleNotYetAvailable;
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp index 6ef0630d..c0772b8 100644 --- a/third_party/WebKit/Source/core/dom/Document.cpp +++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -6563,8 +6563,6 @@ } // namespace blink #ifndef NDEBUG -using namespace blink; - static WeakDocumentSet& liveDocumentSet() { DEFINE_STATIC_LOCAL(WeakDocumentSet, set, ()); return set; @@ -6573,7 +6571,7 @@ void showLiveDocumentInstances() { WeakDocumentSet& set = liveDocumentSet(); fprintf(stderr, "There are %u documents currently alive:\n", set.size()); - for (Document* document : set) + for (blink::Document* document : set) fprintf(stderr, "- Document %p URL: %s\n", document, document->url().getString().utf8().data()); }
diff --git a/third_party/WebKit/Source/core/dom/ElementVisibilityObserver.cpp b/third_party/WebKit/Source/core/dom/ElementVisibilityObserver.cpp index 52676347..98906203 100644 --- a/third_party/WebKit/Source/core/dom/ElementVisibilityObserver.cpp +++ b/third_party/WebKit/Source/core/dom/ElementVisibilityObserver.cpp
@@ -31,8 +31,6 @@ wrapWeakPersistent(this))); DCHECK(m_intersectionObserver); - m_intersectionObserver->setInitialState( - IntersectionObserver::InitialState::kAuto); m_intersectionObserver->observe(m_element.release()); }
diff --git a/third_party/WebKit/Source/core/dom/IntersectionObservation.cpp b/third_party/WebKit/Source/core/dom/IntersectionObservation.cpp index 2d55282..e8e8030 100644 --- a/third_party/WebKit/Source/core/dom/IntersectionObservation.cpp +++ b/third_party/WebKit/Source/core/dom/IntersectionObservation.cpp
@@ -16,7 +16,10 @@ : m_observer(observer), m_target(&target), m_shouldReportRootBounds(shouldReportRootBounds), - m_lastThresholdIndex(0) {} + // Note that the spec says the initial value of m_lastThresholdIndex + // should be -1, but since m_lastThresholdIndex is unsigned, we use a + // different sentinel value. + m_lastThresholdIndex(kMaxThresholdIndex - 1) {} void IntersectionObservation::computeIntersectionObservations( DOMHighResTimeStamp timestamp) { @@ -63,6 +66,9 @@ newVisibleRatio = 0; newThresholdIndex = 0; } + + RELEASE_ASSERT(newThresholdIndex < kMaxThresholdIndex); + if (m_lastThresholdIndex != newThresholdIndex) { IntRect snappedRootBounds = geometry.rootIntRect(); IntRect* rootBoundsPointer =
diff --git a/third_party/WebKit/Source/core/dom/IntersectionObservation.h b/third_party/WebKit/Source/core/dom/IntersectionObservation.h index dcb9323..32f1f25e 100644 --- a/third_party/WebKit/Source/core/dom/IntersectionObservation.h +++ b/third_party/WebKit/Source/core/dom/IntersectionObservation.h
@@ -23,18 +23,21 @@ IntersectionObserver* observer() const { return m_observer.get(); } Element* target() const { return m_target; } unsigned lastThresholdIndex() const { return m_lastThresholdIndex; } - void setLastThresholdIndex(unsigned index) { m_lastThresholdIndex = index; } void computeIntersectionObservations(DOMHighResTimeStamp); void disconnect(); DECLARE_TRACE(); private: + void setLastThresholdIndex(unsigned index) { m_lastThresholdIndex = index; } + Member<IntersectionObserver> m_observer; WeakMember<Element> m_target; const unsigned m_shouldReportRootBounds : 1; + unsigned m_lastThresholdIndex : 30; + static const unsigned kMaxThresholdIndex = (unsigned)0x40000000; }; } // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/IntersectionObserver.cpp b/third_party/WebKit/Source/core/dom/IntersectionObserver.cpp index 408fc2c..779e0a3 100644 --- a/third_party/WebKit/Source/core/dom/IntersectionObserver.cpp +++ b/third_party/WebKit/Source/core/dom/IntersectionObserver.cpp
@@ -173,8 +173,7 @@ m_rightMargin(Fixed), m_bottomMargin(Fixed), m_leftMargin(Fixed), - m_rootIsImplicit(root ? 0 : 1), - m_initialState(InitialState::kHidden) { + m_rootIsImplicit(root ? 0 : 1) { switch (rootMargin.size()) { case 0: break; @@ -272,11 +271,6 @@ "element is not a descendant of root.")); } - if (m_initialState == InitialState::kAuto) { - for (auto& observation : m_observations) - observation->setLastThresholdIndex(std::numeric_limits<unsigned>::max()); - } - if (FrameView* frameView = targetFrame->view()) frameView->scheduleAnimation(); } @@ -314,11 +308,6 @@ m_observations.clear(); } -void IntersectionObserver::setInitialState(InitialState initialState) { - DCHECK(m_observations.isEmpty()); - m_initialState = initialState; -} - HeapVector<Member<IntersectionObserverEntry>> IntersectionObserver::takeRecords( ExceptionState& exceptionState) { HeapVector<Member<IntersectionObserverEntry>> entries;
diff --git a/third_party/WebKit/Source/core/dom/IntersectionObserver.h b/third_party/WebKit/Source/core/dom/IntersectionObserver.h index 894652eacf2..4fb16b61 100644 --- a/third_party/WebKit/Source/core/dom/IntersectionObserver.h +++ b/third_party/WebKit/Source/core/dom/IntersectionObserver.h
@@ -32,21 +32,6 @@ Function<void(const HeapVector<Member<IntersectionObserverEntry>>&), WTF::SameThreadAffinity>; - // Defines the assumed initial state of the observed element. If the actual - // state is the same as the initial state, then no observation will be - // delivered. kAuto means the initial observation will always get sent. - enum InitialState { - // TODO(skyostil): Add support for kVisible. - kAuto = 0, - kHidden = 1, - kDoNotUseMax = 2 - }; - - // InitialState is stored in a single bit in m_initialState. If adding new - // enum values, increase the size of m_initialState and update the assert. - static_assert(InitialState::kDoNotUseMax == 2, - "InitialState fits in a single bit."); - static IntersectionObserver* create(const IntersectionObserverInit&, IntersectionObserverCallback&, ExceptionState&); @@ -93,12 +78,6 @@ return m_observations; } - // Set the assumed initial state of the observed element. Note that this can - // only be set before calling observe(). - // TODO(skyostil): Move this setting to IntersectionObserverInit once the API - // is finalized. - void setInitialState(InitialState); - DECLARE_TRACE(); private: @@ -122,8 +101,6 @@ Length m_bottomMargin; Length m_leftMargin; unsigned m_rootIsImplicit : 1; - // m_initialState contains values from enum InitialState - unsigned m_initialState : 1; }; } // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp index 14b6780..e8d7bb64 100644 --- a/third_party/WebKit/Source/core/frame/FrameView.cpp +++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -2593,7 +2593,7 @@ .enclosingBoundingBox(); } -bool FrameView::isScrollable() { +bool FrameView::isScrollable() const { return getScrollingReasons() == Scrollable; } @@ -2635,6 +2635,9 @@ } void FrameView::updateParentScrollableAreaSet() { + if (RuntimeEnabledFeatures::rootLayerScrollingEnabled()) + return; + // That ensures that only inner frames are cached. FrameView* parentFrameView = this->parentFrameView(); if (!parentFrameView) @@ -3660,7 +3663,8 @@ void FrameView::removeChild(Widget* child) { ASSERT(child->parent() == this); - if (child->isFrameView()) + if (child->isFrameView() && + !RuntimeEnabledFeatures::rootLayerScrollingEnabled()) removeScrollableArea(toFrameView(child)); child->setParent(0); @@ -4889,15 +4893,20 @@ return; reasons |= toLocalFrame(frame).view()->mainThreadScrollingReasonsPerFrame(); - if (WebLayer* scrollLayer = - toLocalFrame(frame).view()->layerForScrolling()->platformLayer()) { - if (reasons) { - scrollLayer->addMainThreadScrollingReasons(reasons); - } else { - // Clear all main thread scrolling reasons except the one that's set - // if there is a running scroll animation. - scrollLayer->clearMainThreadScrollingReasons( - ~MainThreadScrollingReason::kHandlingScrollFromMainThread); + if (GraphicsLayer* layerForScrolling = toLocalFrame(frame) + .view() + ->layoutViewportScrollableArea() + ->layerForScrolling()) { + if (WebLayer* platformLayerForScrolling = + layerForScrolling->platformLayer()) { + if (reasons) { + platformLayerForScrolling->addMainThreadScrollingReasons(reasons); + } else { + // Clear all main thread scrolling reasons except the one that's set + // if there is a running scroll animation. + platformLayerForScrolling->clearMainThreadScrollingReasons( + ~MainThreadScrollingReason::kHandlingScrollFromMainThread); + } } }
diff --git a/third_party/WebKit/Source/core/frame/FrameView.h b/third_party/WebKit/Source/core/frame/FrameView.h index 4400f9e..0bb8fe3 100644 --- a/third_party/WebKit/Source/core/frame/FrameView.h +++ b/third_party/WebKit/Source/core/frame/FrameView.h
@@ -339,7 +339,7 @@ }; ScrollingReasons getScrollingReasons() const; - bool isScrollable(); + bool isScrollable() const override; bool isProgrammaticallyScrollable() override; enum ScrollbarModesCalculationStrategy { RulesFromWebContentOnly, AnyRule };
diff --git a/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicyFuzzer.cpp b/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicyFuzzer.cpp index fdeeba9f..91d4e617 100644 --- a/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicyFuzzer.cpp +++ b/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicyFuzzer.cpp
@@ -9,23 +9,19 @@ #include "platform/testing/BlinkFuzzerTestSupport.h" #include "wtf/text/WTFString.h" -using namespace blink; - -namespace { +namespace blink { // Intentionally leaked during fuzzing. // See testing/libfuzzer/efficient_fuzzer.md. DummyPageHolder* pageHolder = nullptr; -} // namespace - -extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { +int LLVMFuzzerInitialize(int* argc, char*** argv) { InitializeBlinkFuzzTest(argc, argv); pageHolder = DummyPageHolder::create().release(); return 0; } -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { String header = String::fromUTF8(data, size); unsigned hash = header.isNull() ? 0 : header.impl()->hash(); @@ -46,3 +42,13 @@ return 0; } + +} // namespace blink + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + return blink::LLVMFuzzerInitialize(argc, argv); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + return blink::LLVMFuzzerTestOneInput(data, size); +}
diff --git a/third_party/WebKit/Source/core/html/HTMLAttributeNames.in b/third_party/WebKit/Source/core/html/HTMLAttributeNames.in index 697bfa5..6ae73c6 100644 --- a/third_party/WebKit/Source/core/html/HTMLAttributeNames.in +++ b/third_party/WebKit/Source/core/html/HTMLAttributeNames.in
@@ -26,8 +26,10 @@ aria-controls aria-current aria-describedby +aria-details aria-disabled aria-dropeffect +aria-errormessage aria-expanded aria-flowto aria-grabbed @@ -35,6 +37,7 @@ aria-help aria-hidden aria-invalid +aria-keyshortcuts aria-label aria-labeledby aria-labelledby @@ -50,6 +53,7 @@ aria-readonly aria-relevant aria-required +aria-roledescription aria-rowcount aria-rowindex aria-rowspan
diff --git a/third_party/WebKit/Source/core/html/HTMLTableRowElementTest.cpp b/third_party/WebKit/Source/core/html/HTMLTableRowElementTest.cpp index fb581fd..df669b55 100644 --- a/third_party/WebKit/Source/core/html/HTMLTableRowElementTest.cpp +++ b/third_party/WebKit/Source/core/html/HTMLTableRowElementTest.cpp
@@ -9,9 +9,7 @@ #include "core/html/HTMLTableElement.h" #include "testing/gtest/include/gtest/gtest.h" -namespace { - -using namespace blink; +namespace blink { // rowIndex // https://html.spec.whatwg.org/multipage/tables.html#dom-tr-rowindex @@ -46,4 +44,4 @@ << "row index -1"; } -} // namespace +} // namespace blink
diff --git a/third_party/WebKit/Source/core/html/TimeRanges.cpp b/third_party/WebKit/Source/core/html/TimeRanges.cpp index bba54a1..f53d9ba9 100644 --- a/third_party/WebKit/Source/core/html/TimeRanges.cpp +++ b/third_party/WebKit/Source/core/html/TimeRanges.cpp
@@ -30,7 +30,7 @@ #include "core/dom/ExceptionCode.h" #include <math.h> -using namespace blink; +namespace blink { TimeRanges::TimeRanges(double start, double end) { add(start, end); @@ -209,3 +209,5 @@ } return bestMatch; } + +} // namespace blink
diff --git a/third_party/WebKit/Source/core/html/track/TextTrackList.cpp b/third_party/WebKit/Source/core/html/track/TextTrackList.cpp index 6d16b81..5fb96a4 100644 --- a/third_party/WebKit/Source/core/html/track/TextTrackList.cpp +++ b/third_party/WebKit/Source/core/html/track/TextTrackList.cpp
@@ -33,7 +33,7 @@ #include "core/html/track/TextTrack.h" #include "core/html/track/TrackEvent.h" -using namespace blink; +namespace blink { TextTrackList::TextTrackList(HTMLMediaElement* owner) : m_owner(owner), m_asyncEventQueue(GenericEventQueue::create(this)) {} @@ -314,3 +314,5 @@ visitor->traceWrappers(track); EventTargetWithInlineData::traceWrappers(visitor); } + +} // namespace blink
diff --git a/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.cpp index b47890c..6a0401c 100644 --- a/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.cpp +++ b/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.cpp
@@ -90,6 +90,8 @@ #include "wtf/text/CString.h" #include "wtf/text/StringConcatenate.h" +namespace blink { + namespace { int s_frontendOperationCounter = 0; @@ -100,8 +102,6 @@ ~FrontendOperationScope() { --s_frontendOperationCounter; } }; -using namespace blink; - String createShorthandValue(Document* document, const String& shorthand, const String& oldText, @@ -308,8 +308,6 @@ typedef blink::protocol::CSS::Backend::EnableCallback EnableCallback; -namespace blink { - enum ForcePseudoClassFlags { PseudoNone = 0, PseudoHover = 1 << 0,
diff --git a/third_party/WebKit/Source/core/inspector/InspectorStyleSheet.cpp b/third_party/WebKit/Source/core/inspector/InspectorStyleSheet.cpp index 05cbeb1..f5a503c 100644 --- a/third_party/WebKit/Source/core/inspector/InspectorStyleSheet.cpp +++ b/third_party/WebKit/Source/core/inspector/InspectorStyleSheet.cpp
@@ -59,9 +59,9 @@ using blink::protocol::Array; -namespace { +namespace blink { -using namespace blink; +namespace { static const CSSParserContext* parserContextForDocument(Document* document) { return document ? CSSParserContext::create(*document) @@ -680,8 +680,6 @@ } // namespace -namespace blink { - enum MediaListSource { MediaListSourceLinkedSheet, MediaListSourceInlineSheet,
diff --git a/third_party/WebKit/Source/core/inspector/browser_protocol.json b/third_party/WebKit/Source/core/inspector/browser_protocol.json index ad416b6d..6e63064 100644 --- a/third_party/WebKit/Source/core/inspector/browser_protocol.json +++ b/third_party/WebKit/Source/core/inspector/browser_protocol.json
@@ -4293,7 +4293,7 @@ { "id": "AXGlobalStates", "type": "string", - "enum": [ "disabled", "hidden", "hiddenRoot", "invalid" ], + "enum": [ "disabled", "hidden", "hiddenRoot", "invalid", "keyshortcuts", "roledescription" ], "description": "States which apply to every AX node." }, { @@ -4317,7 +4317,7 @@ { "id": "AXRelationshipAttributes", "type": "string", - "enum": [ "activedescendant", "flowto", "controls", "describedby", "labelledby", "owns" ], + "enum": [ "activedescendant", "controls", "describedby", "details", "errormessage", "flowto", "labelledby", "owns" ], "description": "Relationships between elements other than parent/child/sibling." }, {
diff --git a/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp b/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp index bd5f8ce..80076b551 100644 --- a/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp
@@ -1034,8 +1034,7 @@ } else { // Even if we didn't break before the border box to the next // fragmentainer, we need to check if we can fit the margin before it. - if (LayoutUnit pageLogicalHeight = - pageLogicalHeightForOffset(logicalTopMarginEdge)) { + if (pageLogicalHeightForOffset(logicalTopMarginEdge)) { LayoutUnit remainingSpace = pageRemainingLogicalHeightForOffset( logicalTopMarginEdge, AssociateWithLatterPage); if (remainingSpace <= marginBefore)
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp index 42db96d..ebf74c5 100644 --- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -4752,7 +4752,7 @@ // Figure out if we really need to force re-layout of the child. We only need // to do this if there's a chance that we need to recalculate pagination // struts inside. - if (LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalTop)) { + if (pageLogicalHeightForOffset(logicalTop)) { LayoutUnit logicalHeight = child.logicalHeightWithVisibleOverflow(); LayoutUnit remainingSpace = pageRemainingLogicalHeightForOffset( logicalTop, AssociateWithLatterPage);
diff --git a/third_party/WebKit/Source/core/layout/LayoutFullScreen.cpp b/third_party/WebKit/Source/core/layout/LayoutFullScreen.cpp index 20b7d5c..69567cf 100644 --- a/third_party/WebKit/Source/core/layout/LayoutFullScreen.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutFullScreen.cpp
@@ -33,7 +33,9 @@ #include "public/platform/WebScreenInfo.h" -using namespace blink; +namespace blink { + +namespace { class LayoutFullScreenPlaceholder final : public LayoutBlockFlow { public: @@ -61,6 +63,8 @@ LayoutBlockFlow::willBeDestroyed(); } +} // namespace + LayoutFullScreen::LayoutFullScreen() : LayoutFlexibleBox(nullptr), m_placeholder(nullptr) { setIsAtomicInlineLevel(false); @@ -219,3 +223,5 @@ m_placeholder->setStyleWithWritingModeOfParent(std::move(style)); } } + +} // namespace blink
diff --git a/third_party/WebKit/Source/core/loader/appcache/ApplicationCacheHost.cpp b/third_party/WebKit/Source/core/loader/appcache/ApplicationCacheHost.cpp index ad39760..a9002d4 100644 --- a/third_party/WebKit/Source/core/loader/appcache/ApplicationCacheHost.cpp +++ b/third_party/WebKit/Source/core/loader/appcache/ApplicationCacheHost.cpp
@@ -54,8 +54,6 @@ #include "public/platform/WebURLResponse.h" #include "public/platform/WebVector.h" -using namespace blink; - namespace blink { // We provide a custom implementation of this class that calls out to the
diff --git a/third_party/WebKit/Source/core/page/Page.cpp b/third_party/WebKit/Source/core/page/Page.cpp index 4e1bc08..ecfd49d7 100644 --- a/third_party/WebKit/Source/core/page/Page.cpp +++ b/third_party/WebKit/Source/core/page/Page.cpp
@@ -173,21 +173,19 @@ } ClientRectList* Page::nonFastScrollableRects(const LocalFrame* frame) { + DisableCompositingQueryAsserts disabler; if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) { // Hits in compositing/iframes/iframe-composited-scrolling.html - DisableCompositingQueryAsserts disabler; scrollingCoordinator->updateAfterCompositingChangeIfNeeded(); } - if (!frame->view()->layerForScrolling()) + GraphicsLayer* layer = + frame->view()->layoutViewportScrollableArea()->layerForScrolling(); + if (!layer) return ClientRectList::create(); - - // Now retain non-fast scrollable regions - return ClientRectList::create(frame->view() - ->layerForScrolling() - ->platformLayer() - ->nonFastScrollableRegion()); + return ClientRectList::create( + layer->platformLayer()->nonFastScrollableRegion()); } void Page::setMainFrame(Frame* mainFrame) {
diff --git a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp index fb8d5ea..0f6ed92 100644 --- a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp +++ b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
@@ -112,8 +112,10 @@ if (!m_page->mainFrame()->isLocalFrame() || !m_page->deprecatedLocalMainFrame()->view()) return; - if (WebLayer* scrollLayer = toWebLayer( - m_page->deprecatedLocalMainFrame()->view()->layerForScrolling())) { + if (WebLayer* scrollLayer = toWebLayer(m_page->deprecatedLocalMainFrame() + ->view() + ->layoutViewportScrollableArea() + ->layerForScrolling())) { Vector<IntRect> rects = region.rects(); WebVector<WebRect> webRects(rects.size()); for (size_t i = 0; i < rects.size(); ++i) @@ -204,7 +206,8 @@ } FrameView* frameView = toLocalFrame(m_page->mainFrame())->view(); - bool frameIsScrollable = frameView && frameView->isScrollable(); + bool frameIsScrollable = + frameView && frameView->layoutViewportScrollableArea()->isScrollable(); if (m_shouldScrollOnMainThreadDirty || m_wasFrameScrollable != frameIsScrollable) { setShouldUpdateScrollLayerPositionOnMainThread( @@ -219,8 +222,11 @@ m_wasFrameScrollable = frameIsScrollable; if (WebLayer* layoutViewportScrollLayer = - frameView ? toWebLayer(frameView->layerForScrolling()) : nullptr) { - layoutViewportScrollLayer->setBounds(frameView->contentsSize()); + frameView ? toWebLayer(frameView->layoutViewportScrollableArea() + ->layerForScrolling()) + : nullptr) { + if (!RuntimeEnabledFeatures::rootLayerScrollingEnabled()) + layoutViewportScrollLayer->setBounds(frameView->contentsSize()); // If there is a non-root fullscreen element, prevent the viewport from // scrolling. @@ -239,21 +245,25 @@ visualViewportScrollLayer->setUserScrollable(true, true); } - layoutViewportScrollLayer->setUserScrollable( - frameView->userInputScrollable(HorizontalScrollbar), - frameView->userInputScrollable(VerticalScrollbar)); + if (!RuntimeEnabledFeatures::rootLayerScrollingEnabled()) { + layoutViewportScrollLayer->setUserScrollable( + frameView->userInputScrollable(HorizontalScrollbar), + frameView->userInputScrollable(VerticalScrollbar)); + } } - const FrameTree& tree = m_page->mainFrame()->tree(); - for (const Frame* child = tree.firstChild(); child; - child = child->tree().nextSibling()) { - if (!child->isLocalFrame()) - continue; - FrameView* frameView = toLocalFrame(child)->view(); - if (!frameView || frameView->shouldThrottleRendering()) - continue; - if (WebLayer* scrollLayer = toWebLayer(frameView->layerForScrolling())) - scrollLayer->setBounds(frameView->contentsSize()); + if (!RuntimeEnabledFeatures::rootLayerScrollingEnabled()) { + const FrameTree& tree = m_page->mainFrame()->tree(); + for (const Frame* child = tree.firstChild(); child; + child = child->tree().nextSibling()) { + if (!child->isLocalFrame()) + continue; + FrameView* frameView = toLocalFrame(child)->view(); + if (!frameView || frameView->shouldThrottleRendering()) + continue; + if (WebLayer* scrollLayer = toWebLayer(frameView->layerForScrolling())) + scrollLayer->setBounds(frameView->contentsSize()); + } } } @@ -715,8 +725,8 @@ m_wasFrameScrollable = false; m_lastMainThreadScrollingReasons = 0; - setShouldUpdateScrollLayerPositionOnMainThread( - m_lastMainThreadScrollingReasons); + if (!RuntimeEnabledFeatures::rootLayerScrollingEnabled()) + setShouldUpdateScrollLayerPositionOnMainThread(0); } // Note that in principle this could be called more often than @@ -818,19 +828,24 @@ GraphicsLayer* visualViewportLayer = m_page->frameHost().visualViewport().scrollLayer(); WebLayer* visualViewportScrollLayer = toWebLayer(visualViewportLayer); - GraphicsLayer* layer = - m_page->deprecatedLocalMainFrame()->view()->layerForScrolling(); + GraphicsLayer* layer = m_page->deprecatedLocalMainFrame() + ->view() + ->layoutViewportScrollableArea() + ->layerForScrolling(); if (WebLayer* scrollLayer = toWebLayer(layer)) { m_lastMainThreadScrollingReasons = mainThreadScrollingReasons; if (mainThreadScrollingReasons) { - if (ScrollAnimatorBase* scrollAnimator = - layer->getScrollableArea()->existingScrollAnimator()) { - DCHECK(RuntimeEnabledFeatures::slimmingPaintV2Enabled() || - m_page->deprecatedLocalMainFrame() - ->document() - ->lifecycle() - .state() >= DocumentLifecycle::CompositingClean); - scrollAnimator->takeOverCompositorAnimation(); + ScrollableArea* scrollableArea = layer->getScrollableArea(); + if (scrollableArea) { + if (ScrollAnimatorBase* scrollAnimator = + scrollableArea->existingScrollAnimator()) { + DCHECK(RuntimeEnabledFeatures::slimmingPaintV2Enabled() || + m_page->deprecatedLocalMainFrame() + ->document() + ->lifecycle() + .state() >= DocumentLifecycle::CompositingClean); + scrollAnimator->takeOverCompositorAnimation(); + } } scrollLayer->addMainThreadScrollingReasons(mainThreadScrollingReasons); if (visualViewportScrollLayer) { @@ -1152,17 +1167,22 @@ notifyGeometryChanged(); } -bool ScrollingCoordinator::frameViewIsDirty() const { +bool ScrollingCoordinator::frameScrollerIsDirty() const { FrameView* frameView = m_page->mainFrame()->isLocalFrame() ? m_page->deprecatedLocalMainFrame()->view() : nullptr; - bool frameIsScrollable = frameView && frameView->isScrollable(); + bool frameIsScrollable = + frameView && frameView->layoutViewportScrollableArea()->isScrollable(); if (frameIsScrollable != m_wasFrameScrollable) return true; if (WebLayer* scrollLayer = - frameView ? toWebLayer(frameView->layerForScrolling()) : nullptr) - return WebSize(frameView->contentsSize()) != scrollLayer->bounds(); + frameView ? toWebLayer(frameView->layoutViewportScrollableArea() + ->layerForScrolling()) + : nullptr) { + return WebSize(frameView->layoutViewportScrollableArea()->contentsSize()) != + scrollLayer->bounds(); + } return false; }
diff --git a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h index 3b73e61..24f17a5 100644 --- a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h +++ b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h
@@ -154,7 +154,7 @@ private: bool shouldUpdateAfterCompositingChange() const { return m_scrollGestureRegionIsDirty || m_touchEventTargetRectsAreDirty || - m_shouldScrollOnMainThreadDirty || frameViewIsDirty(); + m_shouldScrollOnMainThreadDirty || frameScrollerIsDirty(); } void setShouldUpdateScrollLayerPositionOnMainThread( @@ -171,7 +171,7 @@ ScrollbarOrientation); void removeWebScrollbarLayer(ScrollableArea*, ScrollbarOrientation); - bool frameViewIsDirty() const; + bool frameScrollerIsDirty() const; std::unique_ptr<CompositorAnimationHost> m_animationHost; std::unique_ptr<CompositorAnimationTimeline>
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp index 3d581f8..96d92ae 100644 --- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp +++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
@@ -565,6 +565,10 @@ return IntSize(pixelSnappedScrollWidth(), pixelSnappedScrollHeight()); } +bool PaintLayerScrollableArea::isScrollable() const { + return scrollsOverflow(); +} + IntPoint PaintLayerScrollableArea::lastKnownMousePosition() const { return box().frame() ? box().frame()->eventHandler().lastKnownMousePosition() : IntPoint(); @@ -1669,15 +1673,8 @@ if (!frameView) return; - // FIXME: Does this need to be fixed later for OOPI? bool isVisibleToHitTest = box().style()->visibleToHitTesting(); - if (HTMLFrameOwnerElement* owner = frame->deprecatedLocalOwner()) { - isVisibleToHitTest &= owner->layoutObject() && - owner->layoutObject()->style()->visibleToHitTesting(); - } - bool didScrollOverflow = m_scrollsOverflow; - m_scrollsOverflow = hasOverflow && isVisibleToHitTest; if (didScrollOverflow == scrollsOverflow()) return;
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h index 53a5b52e..87bb281 100644 --- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h +++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h
@@ -280,6 +280,7 @@ int visibleHeight() const override; int visibleWidth() const override; IntSize contentsSize() const override; + bool isScrollable() const override; IntPoint lastKnownMousePosition() const override; bool scrollAnimatorEnabled() const override; bool shouldSuspendScrollAnimations() const override;
diff --git a/third_party/WebKit/Source/core/svg/animation/SMILTime.cpp b/third_party/WebKit/Source/core/svg/animation/SMILTime.cpp index 33a6c58b..9c986cc2 100644 --- a/third_party/WebKit/Source/core/svg/animation/SMILTime.cpp +++ b/third_party/WebKit/Source/core/svg/animation/SMILTime.cpp
@@ -27,12 +27,14 @@ #include <float.h> -using namespace blink; +namespace blink { -SMILTime blink::operator*(const SMILTime& a, const SMILTime& b) { +SMILTime operator*(const SMILTime& a, const SMILTime& b) { // Equal operators have to be used instead of negation here to make NaN work // as well. if (a.value() == 0 || b.value() == 0) return SMILTime(0); return a.value() * b.value(); } + +} // namespace blink
diff --git a/third_party/WebKit/Source/core/testing/v8/WebCoreTestSupport.cpp b/third_party/WebKit/Source/core/testing/v8/WebCoreTestSupport.cpp index 21b58cf..600adbe 100644 --- a/third_party/WebKit/Source/core/testing/v8/WebCoreTestSupport.cpp +++ b/third_party/WebKit/Source/core/testing/v8/WebCoreTestSupport.cpp
@@ -37,8 +37,6 @@ #include "core/testing/Internals.h" #include "core/testing/WorkerInternals.h" -using namespace blink; - namespace WebCoreTestSupport { namespace { @@ -47,15 +45,18 @@ s_originalInstallConditionalFeaturesFunction = nullptr; v8::Local<v8::Value> createInternalsObject(v8::Local<v8::Context> context) { - ScriptState* scriptState = ScriptState::from(context); + blink::ScriptState* scriptState = blink::ScriptState::from(context); v8::Local<v8::Object> global = scriptState->context()->Global(); - ExecutionContext* executionContext = scriptState->getExecutionContext(); + blink::ExecutionContext* executionContext = + scriptState->getExecutionContext(); if (executionContext->isDocument()) { - return ToV8(Internals::create(executionContext), global, - scriptState->isolate()); + return blink::ToV8(blink::Internals::create(executionContext), global, + scriptState->isolate()); } - if (executionContext->isWorkerGlobalScope()) - return ToV8(WorkerInternals::create(), global, scriptState->isolate()); + if (executionContext->isWorkerGlobalScope()) { + return blink::ToV8(blink::WorkerInternals::create(), global, + scriptState->isolate()); + } return v8::Local<v8::Value>(); } } @@ -69,8 +70,8 @@ installConditionalFeaturesForTests); } - ScriptState* scriptState = ScriptState::from(context); - ScriptState::Scope scope(scriptState); + blink::ScriptState* scriptState = blink::ScriptState::from(context); + blink::ScriptState::Scope scope(scriptState); v8::Local<v8::Object> global = scriptState->context()->Global(); v8::Local<v8::Value> internals = createInternalsObject(context); if (internals.IsEmpty()) @@ -78,25 +79,28 @@ global ->Set(scriptState->context(), - v8AtomicString(scriptState->isolate(), "internals"), internals) + blink::v8AtomicString(scriptState->isolate(), "internals"), + internals) .ToChecked(); } void installConditionalFeaturesForTests( - const WrapperTypeInfo* type, + const blink::WrapperTypeInfo* type, const blink::ScriptState* scriptState, v8::Local<v8::Object> prototypeObject, v8::Local<v8::Function> interfaceObject) { (*s_originalInstallConditionalFeaturesFunction)( type, scriptState, prototypeObject, interfaceObject); - ExecutionContext* executionContext = scriptState->getExecutionContext(); - OriginTrialContext* originTrialContext = OriginTrialContext::from( - executionContext, OriginTrialContext::DontCreateIfNotExists); + blink::ExecutionContext* executionContext = + scriptState->getExecutionContext(); + blink::OriginTrialContext* originTrialContext = + blink::OriginTrialContext::from( + executionContext, blink::OriginTrialContext::DontCreateIfNotExists); - if (type == &V8OriginTrialsTest::wrapperTypeInfo) { + if (type == &blink::V8OriginTrialsTest::wrapperTypeInfo) { if (originTrialContext && originTrialContext->isTrialEnabled("Frobulate")) { - V8OriginTrialsTest::installOriginTrialsSampleAPI( + blink::V8OriginTrialsTest::installOriginTrialsSampleAPI( scriptState->isolate(), scriptState->world(), v8::Local<v8::Object>(), prototypeObject, interfaceObject); } @@ -108,19 +112,19 @@ if (context.IsEmpty()) return; - ScriptState* scriptState = ScriptState::from(context); - ScriptState::Scope scope(scriptState); - Document* document = toDocument(scriptState->getExecutionContext()); + blink::ScriptState* scriptState = blink::ScriptState::from(context); + blink::ScriptState::Scope scope(scriptState); + blink::Document* document = toDocument(scriptState->getExecutionContext()); ASSERT(document); - LocalFrame* frame = document->frame(); + blink::LocalFrame* frame = document->frame(); // Should the document have been detached, the page is assumed being destroyed // (=> no reset required.) if (!frame) return; - Page* page = frame->page(); + blink::Page* page = frame->page(); ASSERT(page); - Internals::resetToConsistentState(page); - InternalSettings::from(*page)->resetToConsistentState(); + blink::Internals::resetToConsistentState(page); + blink::InternalSettings::from(*page)->resetToConsistentState(); } } // namespace WebCoreTestSupport
diff --git a/third_party/WebKit/Source/core/xml/XPathParser.cpp b/third_party/WebKit/Source/core/xml/XPathParser.cpp index e7d9a3887..0b65bd02 100644 --- a/third_party/WebKit/Source/core/xml/XPathParser.cpp +++ b/third_party/WebKit/Source/core/xml/XPathParser.cpp
@@ -37,7 +37,8 @@ #include "wtf/StdLibExtras.h" #include "wtf/text/StringHash.h" -using namespace blink; +namespace blink { + using namespace WTF; using namespace Unicode; using namespace XPath; @@ -517,3 +518,5 @@ DCHECK(m_strings.contains(s)); m_strings.remove(s); } + +} // namespace blink
diff --git a/third_party/WebKit/Source/devtools/BUILD.gn b/third_party/WebKit/Source/devtools/BUILD.gn index 3787268..5bfa86f 100644 --- a/third_party/WebKit/Source/devtools/BUILD.gn +++ b/third_party/WebKit/Source/devtools/BUILD.gn
@@ -403,6 +403,7 @@ "front_end/resources/ServiceWorkerCacheViews.js", "front_end/resources/serviceWorkersView.css", "front_end/resources/ServiceWorkersView.js", + "front_end/resources/StorageItemsView.js", "front_end/Runtime.js", "front_end/sass/ASTService.js", "front_end/sass/ASTSourceMap.js",
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/CookieItemsView.js b/third_party/WebKit/Source/devtools/front_end/resources/CookieItemsView.js index 3043976..cfc8083 100644 --- a/third_party/WebKit/Source/devtools/front_end/resources/CookieItemsView.js +++ b/third_party/WebKit/Source/devtools/front_end/resources/CookieItemsView.js
@@ -27,71 +27,75 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/** - * @unrestricted - */ -Resources.CookieItemsView = class extends UI.SimpleView { +Resources.CookieItemsView = class extends Resources.StorageItemsView { + /** + * @param {!Resources.CookieTreeElement} treeElement + * @param {!SDK.Target} target + * @param {string} cookieDomain + */ constructor(treeElement, target, cookieDomain) { - super(Common.UIString('Cookies')); + super(Common.UIString('Cookies'), 'cookiesPanel'); this.element.classList.add('storage-view'); - this._deleteButton = new UI.ToolbarButton(Common.UIString('Delete Selected'), 'largeicon-delete'); - this._deleteButton.addEventListener(UI.ToolbarButton.Events.Click, this._deleteButtonClicked, this); - - this._clearButton = new UI.ToolbarButton(Common.UIString('Clear All'), 'largeicon-clear'); - this._clearButton.addEventListener(UI.ToolbarButton.Events.Click, this._clearButtonClicked, this); - - this._refreshButton = new UI.ToolbarButton(Common.UIString('Refresh'), 'largeicon-refresh'); - this._refreshButton.addEventListener(UI.ToolbarButton.Events.Click, this._refreshButtonClicked, this); - - this._filterBar = new UI.FilterBar('cookiesPanel', true); - this._textFilterUI = new UI.TextFilterUI(true); - this._textFilterUI.addEventListener(UI.FilterUI.Events.FilterChanged, this._filterChanged, this); - this._filterBar.addFilter(this._textFilterUI); - - this._filterSeparator = new UI.ToolbarSeparator(); - this._filterButton = this._filterBar.filterButton(); - this._target = target; this._treeElement = treeElement; this._cookieDomain = cookieDomain; - this.element.addEventListener('contextmenu', this._contextMenu.bind(this), true); + /** @type {?Array<!SDK.Cookie>} */ + this._cookies = null; + this._totalSize = 0; + /** @type {?CookieTable.CookiesTable} */ + this._cookiesTable = null; } /** - * @override - * @return {!Array.<!UI.ToolbarItem>} + * @param {!Array.<!SDK.Cookie>} allCookies */ - syncToolbarItems() { - return [this._refreshButton, this._clearButton, this._deleteButton, this._filterSeparator, this._filterButton]; + _updateWithCookies(allCookies) { + this._cookies = allCookies; + this._totalSize = allCookies.reduce((size, cookie) => size + cookie.size(), 0); + + if (!this._cookiesTable) { + const parsedURL = this._cookieDomain.asParsedURL(); + const domain = parsedURL ? parsedURL.host : ''; + this._cookiesTable = new CookieTable.CookiesTable( + false, this.refreshItems.bind(this), () => this.setCanDeleteSelected(true), domain); + } + + var shownCookies = this.filter(allCookies, cookie => `${cookie.name()} ${cookie.value()} ${cookie.domain()}`); + this._cookiesTable.setCookies(shownCookies); + this._cookiesTable.show(this.element); + this._treeElement.subtitle = + String.sprintf(Common.UIString('%d cookies (%s)'), this._cookies.length, Number.bytesToString(this._totalSize)); + this.setCanFilter(true); + this.setCanDeleteAll(true); + this.setCanDeleteSelected(!!this._cookiesTable.selectedCookie()); } /** * @override */ - wasShown() { - this._update(); + deleteAllItems() { + this._cookiesTable.clear(); + this.refreshItems(); } /** * @override */ - willHide() { - this._deleteButton.setEnabled(false); + deleteSelectedItem() { + var selectedCookie = this._cookiesTable.selectedCookie(); + if (selectedCookie) { + selectedCookie.remove(); + this.refreshItems(); + } } /** - * @param {!Common.Event} event + * @override */ - _filterChanged(event) { - var text = this._textFilterUI.value(); - this._filterRegex = text && new RegExp(text.escapeForRegExp(), 'i'); - this._update(); - } - - _update() { + refreshItems() { var resourceURLs = []; var cookieDomain = this._cookieDomain; /** @@ -106,84 +110,4 @@ SDK.ResourceTreeModel.fromTarget(this._target).forAllResources(populateResourceURLs); SDK.Cookies.getCookiesAsync(this._target, resourceURLs, this._updateWithCookies.bind(this)); } - - /** - * @param {!Array.<!SDK.Cookie>} allCookies - */ - _updateWithCookies(allCookies) { - this._cookies = allCookies; - this._totalSize = allCookies.reduce((size, cookie) => size + cookie.size(), 0); - - if (!this._cookiesTable) { - const parsedURL = this._cookieDomain.asParsedURL(); - const domain = parsedURL ? parsedURL.host : ''; - this._cookiesTable = - new CookieTable.CookiesTable(false, this._update.bind(this), this._enableDeleteButton.bind(this), domain); - } - - var shownCookies = this._filterCookies(this._cookies); - this._cookiesTable.setCookies(shownCookies); - this._filterBar.show(this.element); - this._cookiesTable.show(this.element); - this._treeElement.subtitle = - String.sprintf(Common.UIString('%d cookies (%s)'), this._cookies.length, Number.bytesToString(this._totalSize)); - this._filterButton.setEnabled(true); - this._clearButton.setEnabled(true); - this._deleteButton.setEnabled(!!this._cookiesTable.selectedCookie()); - } - - /** - * @param {!Array.<!SDK.Cookie>} cookies - */ - _filterCookies(cookies) { - if (!this._filterRegex) - return cookies; - - return cookies.filter(cookie => { - const candidate = `${cookie.name()} ${cookie.value()} ${cookie.domain()}`; - return this._filterRegex.test(candidate); - }); - } - - clear() { - this._cookiesTable.clear(); - this._update(); - } - - /** - * @param {!Common.Event} event - */ - _clearButtonClicked(event) { - this.clear(); - } - - _enableDeleteButton() { - this._deleteButton.setEnabled(true); - } - - /** - * @param {!Common.Event} event - */ - _deleteButtonClicked(event) { - var selectedCookie = this._cookiesTable.selectedCookie(); - if (selectedCookie) { - selectedCookie.remove(); - this._update(); - } - } - - /** - * @param {!Common.Event} event - */ - _refreshButtonClicked(event) { - this._update(); - } - - _contextMenu(event) { - if (!this._cookies.length) { - var contextMenu = new UI.ContextMenu(event); - contextMenu.appendItem(Common.UIString('Refresh'), this._update.bind(this)); - contextMenu.show(); - } - } };
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/DOMStorageItemsView.js b/third_party/WebKit/Source/devtools/front_end/resources/DOMStorageItemsView.js index 6e36741..22e6266e 100644 --- a/third_party/WebKit/Source/devtools/front_end/resources/DOMStorageItemsView.js +++ b/third_party/WebKit/Source/devtools/front_end/resources/DOMStorageItemsView.js
@@ -24,59 +24,34 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/** - * @unrestricted - */ -Resources.DOMStorageItemsView = class extends UI.SimpleView { +Resources.DOMStorageItemsView = class extends Resources.StorageItemsView { + /** + * @param {!Resources.DOMStorage} domStorage + */ constructor(domStorage) { - super(Common.UIString('DOM Storage')); + super(Common.UIString('DOM Storage'), 'domStoragePanel'); - this.domStorage = domStorage; + this._domStorage = domStorage; this.element.classList.add('storage-view', 'table'); - this._deleteButton = new UI.ToolbarButton(Common.UIString('Delete'), 'largeicon-delete'); - this._deleteButton.setEnabled(false); - this._deleteButton.addEventListener(UI.ToolbarButton.Events.Click, this._deleteButtonClicked, this); - - this._clearButton = new UI.ToolbarButton(Common.UIString('Clear All'), 'largeicon-clear'); - this._clearButton.addEventListener(UI.ToolbarButton.Events.Click, () => this.domStorage.clear(), this); - - this._refreshButton = new UI.ToolbarButton(Common.UIString('Refresh'), 'largeicon-refresh'); - this._refreshButton.addEventListener(UI.ToolbarButton.Events.Click, this._refreshButtonClicked, this); - - this.domStorage.addEventListener( + var columns = /** @type {!Array<!DataGrid.DataGrid.ColumnDescriptor>} */ ([ + {id: 'key', title: Common.UIString('Key'), sortable: false, editable: true, weight: 50}, + {id: 'value', title: Common.UIString('Value'), sortable: false, editable: true, weight: 50} + ]); + this._dataGrid = new DataGrid.DataGrid(columns, this._editingCallback.bind(this), this._deleteCallback.bind(this)); + this._dataGrid.setName('DOMStorageItemsView'); + this._dataGrid.asWidget().show(this.element); + this._domStorage.addEventListener( Resources.DOMStorage.Events.DOMStorageItemsCleared, this._domStorageItemsCleared, this); - this.domStorage.addEventListener( + this._domStorage.addEventListener( Resources.DOMStorage.Events.DOMStorageItemRemoved, this._domStorageItemRemoved, this); - this.domStorage.addEventListener(Resources.DOMStorage.Events.DOMStorageItemAdded, this._domStorageItemAdded, this); - this.domStorage.addEventListener( + this._domStorage.addEventListener(Resources.DOMStorage.Events.DOMStorageItemAdded, this._domStorageItemAdded, this); + this._domStorage.addEventListener( Resources.DOMStorage.Events.DOMStorageItemUpdated, this._domStorageItemUpdated, this); } /** - * @override - * @return {!Array.<!UI.ToolbarItem>} - */ - syncToolbarItems() { - return [this._refreshButton, this._clearButton, this._deleteButton]; - } - - /** - * @override - */ - wasShown() { - this._update(); - } - - /** - * @override - */ - willHide() { - this._deleteButton.setEnabled(false); - } - - /** * @param {!Common.Event} event */ _domStorageItemsCleared(event) { @@ -85,7 +60,7 @@ this._dataGrid.rootNode().removeChildren(); this._dataGrid.addCreationNode(false); - this._deleteButton.setEnabled(false); + this.setCanDeleteSelected(false); } /** @@ -103,7 +78,7 @@ var childNode = children[i]; if (childNode.data.key === storageData.key) { rootNode.removeChild(childNode); - this._deleteButton.setEnabled(children.length > 1); + this.setCanDeleteSelected(children.length > 1); return; } } @@ -120,7 +95,7 @@ var rootNode = this._dataGrid.rootNode(); var children = rootNode.children; - this._deleteButton.setEnabled(true); + this.setCanDeleteSelected(true); for (var i = 0; i < children.length; ++i) { if (children[i].data.key === storageData.key) @@ -157,59 +132,48 @@ childNode.select(); childNode.reveal(); } - this._deleteButton.setEnabled(true); + this.setCanDeleteSelected(true); } } } - _update() { - this.detachChildWidgets(); - this.domStorage.getItems(this._showDOMStorageItems.bind(this)); - } - + /** + * @param {?string} error + * @param {!Array<!Array<string>>} items + */ _showDOMStorageItems(error, items) { if (error) return; - - this._dataGrid = this._dataGridForDOMStorageItems(items); - this._dataGrid.asWidget().show(this.element); - this._deleteButton.setEnabled(this._dataGrid.rootNode().children.length > 1); - } - - _dataGridForDOMStorageItems(items) { - var columns = /** @type {!Array<!DataGrid.DataGrid.ColumnDescriptor>} */ ([ - {id: 'key', title: Common.UIString('Key'), sortable: false, editable: true, weight: 50}, - {id: 'value', title: Common.UIString('Value'), sortable: false, editable: true, weight: 50} - ]); - - var nodes = []; - - var keys = []; - var length = items.length; - for (var i = 0; i < items.length; i++) { - var key = items[i][0]; - var value = items[i][1]; + var rootNode = this._dataGrid.rootNode(); + var selectedKey = null; + for (var node of rootNode.children) { + if (!node.selected) + continue; + selectedKey = node.data.key; + break; + } + rootNode.removeChildren(); + var selectedNode = null; + var filteredItems = item => `${item[0]} ${item[1]}`; + for (var item of this.filter(items, filteredItems)) { + var key = item[0]; + var value = item[1]; var node = new DataGrid.DataGridNode({key: key, value: value}, false); node.selectable = true; - nodes.push(node); - keys.push(key); + rootNode.appendChild(node); + if (!selectedNode || key === selectedKey) + selectedNode = node; } - - var dataGrid = new DataGrid.DataGrid(columns, this._editingCallback.bind(this), this._deleteCallback.bind(this)); - dataGrid.setName('DOMStorageItemsView'); - length = nodes.length; - for (var i = 0; i < length; ++i) - dataGrid.rootNode().appendChild(nodes[i]); - dataGrid.addCreationNode(false); - if (length > 0) - nodes[0].selected = true; - return dataGrid; + if (selectedNode) + selectedNode.selected = true; + this._dataGrid.addCreationNode(false); + this.setCanDeleteSelected(!!selectedNode); } /** - * @param {!Common.Event} event + * @override */ - _deleteButtonClicked(event) { + deleteSelectedItem() { if (!this._dataGrid || !this._dataGrid.selectedNode) return; @@ -217,14 +181,21 @@ } /** - * @param {!Common.Event} event + * @override */ - _refreshButtonClicked(event) { - this._update(); + refreshItems() { + this._domStorage.getItems((error, items) => this._showDOMStorageItems(error, items)); + } + + /** + * @override + */ + deleteAllItems() { + this._domStorage.clear(); } _editingCallback(editingNode, columnIdentifier, oldText, newText) { - var domStorage = this.domStorage; + var domStorage = this._domStorage; if (columnIdentifier === 'key') { if (typeof oldText === 'string') domStorage.removeItem(oldText); @@ -252,7 +223,7 @@ if (!node || node.isCreationNode) return; - if (this.domStorage) - this.domStorage.removeItem(node.data.key); + if (this._domStorage) + this._domStorage.removeItem(node.data.key); } };
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/ResourcesPanel.js b/third_party/WebKit/Source/devtools/front_end/resources/ResourcesPanel.js index 5b28d04..948c20f 100644 --- a/third_party/WebKit/Source/devtools/front_end/resources/ResourcesPanel.js +++ b/third_party/WebKit/Source/devtools/front_end/resources/ResourcesPanel.js
@@ -91,8 +91,8 @@ this.resourcesListTreeElement = this._addSidebarSection(Common.UIString('Frames')); var mainContainer = new UI.VBox(); - this._storageViewToolbar = new UI.Toolbar('resources-toolbar', mainContainer.element); this.storageViews = mainContainer.element.createChild('div', 'vbox flex-auto'); + this._storageViewToolbar = new UI.Toolbar('resources-toolbar', mainContainer.element); this.splitWidget().setMainWidget(mainContainer); /** @type {!Map.<!Resources.Database, !Object.<string, !Resources.DatabaseTableView>>} */ @@ -597,7 +597,7 @@ /** * @param {!Resources.CookieTreeElement} treeElement * @param {string} cookieDomain - * @param {!SDK.ResourceTreeFrame} cookieFrameTarget + * @param {!SDK.Target} cookieFrameTarget */ showCookies(treeElement, cookieDomain, cookieFrameTarget) { var view = this._cookieViews[cookieDomain]; @@ -614,7 +614,7 @@ */ clearCookies(cookieDomain) { if (this._cookieViews[cookieDomain]) - this._cookieViews[cookieDomain].clear(); + this._cookieViews[cookieDomain].deleteAllItems(); } showApplicationCache(frameId) {
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/StorageItemsView.js b/third_party/WebKit/Source/devtools/front_end/resources/StorageItemsView.js new file mode 100644 index 0000000..a404165 --- /dev/null +++ b/third_party/WebKit/Source/devtools/front_end/resources/StorageItemsView.js
@@ -0,0 +1,138 @@ +// 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. + +Resources.StorageItemsView = class extends UI.VBox { + /** + * @param {string} title + * @param {string} filterName + */ + constructor(title, filterName) { + super(false); + /** @type {?RegExp} */ + this._filterRegex = null; + + this._filterBar = new UI.FilterBar(filterName, true); + this._textFilterUI = new UI.TextFilterUI(false); + this._textFilterUI.addEventListener(UI.FilterUI.Events.FilterChanged, this._filterChanged, this); + this._filterBar.addFilter(this._textFilterUI); + + this._deleteAllButton = this._addButton(Common.UIString('Clear All'), 'largeicon-clear', this.deleteAllItems); + this._deleteSelectedButton = + this._addButton(Common.UIString('Delete Selected'), 'largeicon-delete', this.deleteSelectedItem); + this._refreshButton = this._addButton(Common.UIString('Refresh'), 'largeicon-refresh', this.refreshItems); + this._filterButton = this._filterBar.filterButton(); + + this._mainToolbar = new UI.Toolbar('top-resources-toolbar', this.element); + + var toolbarItems = [ + this._refreshButton, this._deleteAllButton, this._deleteSelectedButton, new UI.ToolbarSeparator(), + this._filterButton + ]; + + this.element.addEventListener('contextmenu', this._showContextMenu.bind(this), true); + + for (var item of toolbarItems) + this._mainToolbar.appendToolbarItem(item); + + this._filterBar.show(this.element); + } + + /** + * @param {string} label + * @param {string} glyph + * @param {!Function} callback + * @return {!UI.ToolbarButton} + */ + _addButton(label, glyph, callback) { + var button = new UI.ToolbarButton(label, glyph); + button.addEventListener(UI.ToolbarButton.Events.Click, callback, this); + return button; + } + + /** + * @param {!Event} event + */ + _showContextMenu(event) { + var contextMenu = new UI.ContextMenu(event); + contextMenu.appendItem(Common.UIString('Refresh'), this.refreshItems.bind(this)); + contextMenu.show(); + } + + + /** + * @param {!Common.Event} event + */ + _filterChanged(event) { + var text = this._textFilterUI.value(); + this._filterRegex = text ? new RegExp(text.escapeForRegExp(), 'i') : null; + this.refreshItems(); + } + + /** + * @param {!Array<?Object>} items + * @param {function(?Object): string} keyFunction + * @return {!Array<?Object>} + * @protected + */ + filter(items, keyFunction) { + if (!this._filterRegex) + return items; + return items.filter(item => this._filterRegex.test(keyFunction(item))); + } + + /** + * @override + */ + wasShown() { + this.refreshItems(); + } + + /** + * @override + */ + willHide() { + this.setCanDeleteSelected(false); + } + + /** + * @param {boolean} enabled + * @protected + */ + setCanDeleteAll(enabled) { + this._deleteAllButton.setEnabled(enabled); + } + + /** + * @param {boolean} enabled + * @protected + */ + setCanDeleteSelected(enabled) { + this._deleteSelectedButton.setEnabled(enabled); + } + + /** + * @param {boolean} enabled + * @protected + */ + setCanRefresh(enabled) { + this._refreshButton.setEnabled(enabled); + } + + /** + * @param {boolean} enabled + * @protected + */ + setCanFilter(enabled) { + this._filterButton.setEnabled(enabled); + } + + deleteAllItems() { + } + + deleteSelectedItem() { + } + + refreshItems() { + } +};
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/module.json b/third_party/WebKit/Source/devtools/front_end/resources/module.json index 8d45eee..4ffb811 100644 --- a/third_party/WebKit/Source/devtools/front_end/resources/module.json +++ b/third_party/WebKit/Source/devtools/front_end/resources/module.json
@@ -28,6 +28,7 @@ "AppManifestView.js", "ApplicationCacheItemsView.js", "ClearStorageView.js", + "StorageItemsView.js", "CookieItemsView.js", "DatabaseModel.js", "DOMStorageModel.js",
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/resourcesPanel.css b/third_party/WebKit/Source/devtools/front_end/resources/resourcesPanel.css index e75321c97..89b61c2 100644 --- a/third_party/WebKit/Source/devtools/front_end/resources/resourcesPanel.css +++ b/third_party/WebKit/Source/devtools/front_end/resources/resourcesPanel.css
@@ -28,6 +28,11 @@ */ .resources-toolbar { + border-top: 1px solid #ccc; + background-color: #f3f3f3; +} + +.top-resources-toolbar { border-bottom: 1px solid #ccc; background-color: #f3f3f3; }
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/Tooltip.js b/third_party/WebKit/Source/devtools/front_end/ui/Tooltip.js index 047eefe..9b8d07d1 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui/Tooltip.js +++ b/third_party/WebKit/Source/devtools/front_end/ui/Tooltip.js
@@ -114,10 +114,7 @@ // Get container element. var container = UI.Dialog.modalHostView().element; - if (!anchorElement.isDescendant(container)) - container = this.element.parentElement; - - // Posititon tooltip based on the anchor element. + // Position tooltip based on the anchor element. var containerBox = container.boxInWindow(this.element.window()); var anchorBox = this._anchorElement.boxInWindow(this.element.window()); const anchorOffset = 2;
diff --git a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp index 77979a5..eb78cb1 100644 --- a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp +++ b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp
@@ -193,6 +193,18 @@ axSparseAttributeSetterMap.set( aria_flowtoAttr, new ObjectVectorAttributeSetter(AXObjectVectorAttribute::AriaFlowTo)); + axSparseAttributeSetterMap.set( + aria_detailsAttr, + new ObjectVectorAttributeSetter(AXObjectVectorAttribute::AriaDetails)); + axSparseAttributeSetterMap.set( + aria_errormessageAttr, + new ObjectAttributeSetter(AXObjectAttribute::AriaErrorMessage)); + axSparseAttributeSetterMap.set( + aria_keyshortcutsAttr, + new StringAttributeSetter(AXStringAttribute::AriaKeyShortcuts)); + axSparseAttributeSetterMap.set( + aria_roledescriptionAttr, + new StringAttributeSetter(AXStringAttribute::AriaRoleDescription)); } return axSparseAttributeSetterMap; }
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.h b/third_party/WebKit/Source/modules/accessibility/AXObject.h index 1961634..911958b 100644 --- a/third_party/WebKit/Source/modules/accessibility/AXObject.h +++ b/third_party/WebKit/Source/modules/accessibility/AXObject.h
@@ -328,14 +328,19 @@ enum class AXBoolAttribute {}; -enum class AXStringAttribute {}; +enum class AXStringAttribute { + AriaKeyShortcuts, + AriaRoleDescription, +}; enum class AXObjectAttribute { AriaActiveDescendant, + AriaErrorMessage, }; enum class AXObjectVectorAttribute { AriaControls, + AriaDetails, AriaFlowTo, };
diff --git a/third_party/WebKit/Source/modules/accessibility/InspectorAccessibilityAgent.cpp b/third_party/WebKit/Source/modules/accessibility/InspectorAccessibilityAgent.cpp index 235cd9b..8df3281 100644 --- a/third_party/WebKit/Source/modules/accessibility/InspectorAccessibilityAgent.cpp +++ b/third_party/WebKit/Source/modules/accessibility/InspectorAccessibilityAgent.cpp
@@ -341,9 +341,24 @@ Member<AXObject> m_axObject; protocol::Array<AXProperty>& m_properties; - void addBoolAttribute(AXBoolAttribute attribute, bool value) {} + void addBoolAttribute(AXBoolAttribute attribute, bool value) { + // Implement this when we add the first sparse bool attribute. + } - void addStringAttribute(AXStringAttribute attribute, const String& value) {} + void addStringAttribute(AXStringAttribute attribute, const String& value) { + switch (attribute) { + case AXStringAttribute::AriaKeyShortcuts: + m_properties.addItem( + createProperty(AXGlobalStatesEnum::Keyshortcuts, + createValue(value, AXValueTypeEnum::String))); + break; + case AXStringAttribute::AriaRoleDescription: + m_properties.addItem( + createProperty(AXGlobalStatesEnum::Roledescription, + createValue(value, AXValueTypeEnum::String))); + break; + } + } void addObjectAttribute(AXObjectAttribute attribute, AXObject& object) { switch (attribute) { @@ -352,6 +367,11 @@ createProperty(AXRelationshipAttributesEnum::Activedescendant, createRelatedNodeListValue(object))); break; + case AXObjectAttribute::AriaErrorMessage: + m_properties.addItem( + createProperty(AXRelationshipAttributesEnum::Errormessage, + createRelatedNodeListValue(object))); + break; } } @@ -363,6 +383,11 @@ AXRelationshipAttributesEnum::Controls, objects, aria_controlsAttr, *m_axObject)); break; + case AXObjectVectorAttribute::AriaDetails: + m_properties.addItem(createRelatedNodeListProperty( + AXRelationshipAttributesEnum::Details, objects, aria_controlsAttr, + *m_axObject)); + break; case AXObjectVectorAttribute::AriaFlowTo: m_properties.addItem(createRelatedNodeListProperty( AXRelationshipAttributesEnum::Flowto, objects, aria_flowtoAttr,
diff --git a/third_party/WebKit/Source/modules/compositorworker/OWNERS b/third_party/WebKit/Source/modules/compositorworker/OWNERS index 95bfcce..331e182 100644 --- a/third_party/WebKit/Source/modules/compositorworker/OWNERS +++ b/third_party/WebKit/Source/modules/compositorworker/OWNERS
@@ -3,3 +3,5 @@ # for Worker related changes kinuko@chromium.org nhiroki@chromium.org + +# COMPONENT: Internals>Compositing>Animation
diff --git a/third_party/WebKit/Source/modules/filesystem/OWNERS b/third_party/WebKit/Source/modules/filesystem/OWNERS index b0f6e9d..1ac318b 100644 --- a/third_party/WebKit/Source/modules/filesystem/OWNERS +++ b/third_party/WebKit/Source/modules/filesystem/OWNERS
@@ -2,3 +2,6 @@ michaeln@chromium.org nhiroki@chromium.org tzik@chromium.org + +# TEAM: storage-dev@chromium.org +# COMPONENT: Blink>Storage>FileSystem
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp index b69ab6b..974de92 100644 --- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp +++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
@@ -370,10 +370,6 @@ } void ImageCapture::onServiceConnectionError() { - if (!Platform::current()) { - // TODO(rockot): Clean this up once renderer shutdown sequence is fixed. - return; - } m_service.reset(); for (ScriptPromiseResolver* resolver : m_serviceRequests) resolver->reject(DOMException::create(NotFoundError, kNoServiceError));
diff --git a/third_party/WebKit/Source/modules/netinfo/NetworkInformation.cpp b/third_party/WebKit/Source/modules/netinfo/NetworkInformation.cpp index 5451e1db..e7cadfbf 100644 --- a/third_party/WebKit/Source/modules/netinfo/NetworkInformation.cpp +++ b/third_party/WebKit/Source/modules/netinfo/NetworkInformation.cpp
@@ -11,9 +11,9 @@ #include "platform/RuntimeEnabledFeatures.h" #include "wtf/text/WTFString.h" -namespace { +namespace blink { -using namespace blink; +namespace { String connectionTypeToString(WebConnectionType type) { switch (type) { @@ -42,8 +42,6 @@ } // namespace -namespace blink { - NetworkInformation* NetworkInformation::create(ExecutionContext* context) { return new NetworkInformation(context); }
diff --git a/third_party/WebKit/Source/modules/nfc/NFC.cpp b/third_party/WebKit/Source/modules/nfc/NFC.cpp index 7125180..6c3f758 100644 --- a/third_party/WebKit/Source/modules/nfc/NFC.cpp +++ b/third_party/WebKit/Source/modules/nfc/NFC.cpp
@@ -746,11 +746,6 @@ } void NFC::OnConnectionError() { - if (!Platform::current()) { - // TODO(rockot): Clean this up once renderer shutdown sequence is fixed. - return; - } - m_nfc.reset(); m_callbacks.clear();
diff --git a/third_party/WebKit/Source/modules/notifications/NotificationManager.cpp b/third_party/WebKit/Source/modules/notifications/NotificationManager.cpp index f3951aa..c3a8938 100644 --- a/third_party/WebKit/Source/modules/notifications/NotificationManager.cpp +++ b/third_party/WebKit/Source/modules/notifications/NotificationManager.cpp
@@ -100,10 +100,6 @@ } void NotificationManager::onPermissionServiceConnectionError() { - if (!Platform::current()) { - // TODO(rockot): Clean this up once renderer shutdown sequence is fixed. - return; - } m_permissionService.reset(); }
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp index 4ccc45d..53dea7a 100644 --- a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp +++ b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
@@ -945,11 +945,6 @@ } void PaymentRequest::OnError(PaymentErrorReason error) { - if (!Platform::current()) { - // TODO(rockot): Clean this up once renderer shutdown sequence is fixed. - return; - } - bool isError = false; ExceptionCode ec = UnknownError; String message;
diff --git a/third_party/WebKit/Source/modules/quota/StorageManager.cpp b/third_party/WebKit/Source/modules/quota/StorageManager.cpp index ed5c5f2..2830ea3e 100644 --- a/third_party/WebKit/Source/modules/quota/StorageManager.cpp +++ b/third_party/WebKit/Source/modules/quota/StorageManager.cpp
@@ -150,11 +150,6 @@ } void StorageManager::permissionServiceConnectionError() { - if (!Platform::current()) { - // TODO(rockot): Clean this up once renderer shutdown sequence is fixed. - return; - } - m_permissionService.reset(); }
diff --git a/third_party/WebKit/Source/modules/sensor/SensorProviderProxy.cpp b/third_party/WebKit/Source/modules/sensor/SensorProviderProxy.cpp index 17bb5a0..24cab8b 100644 --- a/third_party/WebKit/Source/modules/sensor/SensorProviderProxy.cpp +++ b/third_party/WebKit/Source/modules/sensor/SensorProviderProxy.cpp
@@ -76,11 +76,6 @@ } void SensorProviderProxy::onSensorProviderConnectionError() { - if (!Platform::current()) { - // TODO(rockot): Clean this up once renderer shutdown sequence is fixed. - return; - } - m_sensorProvider.reset(); for (SensorProxy* sensor : m_sensorProxies) sensor->handleSensorError();
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioParam.cpp b/third_party/WebKit/Source/modules/webaudio/AudioParam.cpp index 10621463..e96eed5 100644 --- a/third_party/WebKit/Source/modules/webaudio/AudioParam.cpp +++ b/third_party/WebKit/Source/modules/webaudio/AudioParam.cpp
@@ -517,10 +517,9 @@ return this; } -AudioParam* AudioParam::cancelValuesAndHoldAtTime( - double startTime, - ExceptionState& exceptionState) { - handler().timeline().cancelValuesAndHoldAtTime(startTime, exceptionState); +AudioParam* AudioParam::cancelAndHoldAtTime(double startTime, + ExceptionState& exceptionState) { + handler().timeline().cancelAndHoldAtTime(startTime, exceptionState); return this; }
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioParam.h b/third_party/WebKit/Source/modules/webaudio/AudioParam.h index cf2f761..6726de7 100644 --- a/third_party/WebKit/Source/modules/webaudio/AudioParam.h +++ b/third_party/WebKit/Source/modules/webaudio/AudioParam.h
@@ -251,7 +251,7 @@ double duration, ExceptionState&); AudioParam* cancelScheduledValues(double startTime, ExceptionState&); - AudioParam* cancelValuesAndHoldAtTime(double startTime, ExceptionState&); + AudioParam* cancelAndHoldAtTime(double startTime, ExceptionState&); private: AudioParam(BaseAudioContext&,
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioParam.idl b/third_party/WebKit/Source/modules/webaudio/AudioParam.idl index 1cc12ba..f604805 100644 --- a/third_party/WebKit/Source/modules/webaudio/AudioParam.idl +++ b/third_party/WebKit/Source/modules/webaudio/AudioParam.idl
@@ -51,6 +51,6 @@ [RaisesException, MeasureAs=AudioParamCancelScheduledValues] AudioParam cancelScheduledValues(double startTime); // Cancel scheduled parameter changes and hold the last value - [RaisesException] AudioParam cancelValuesAndHoldAtTime(double startTime); + [RaisesException] AudioParam cancelAndHoldAtTime(double startTime); };
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.cpp b/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.cpp index eacec0b..3d12a3f9 100644 --- a/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.cpp +++ b/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.cpp
@@ -618,9 +618,8 @@ } } -void AudioParamTimeline::cancelValuesAndHoldAtTime( - double cancelTime, - ExceptionState& exceptionState) { +void AudioParamTimeline::cancelAndHoldAtTime(double cancelTime, + ExceptionState& exceptionState) { DCHECK(isMainThread()); if (!isNonNegativeAudioParamTime(cancelTime, exceptionState))
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.h b/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.h index 7899745..6e6ef7b 100644 --- a/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.h +++ b/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.h
@@ -64,7 +64,7 @@ double duration, ExceptionState&); void cancelScheduledValues(double startTime, ExceptionState&); - void cancelValuesAndHoldAtTime(double cancelTime, ExceptionState&); + void cancelAndHoldAtTime(double cancelTime, ExceptionState&); // hasValue is set to true if a valid timeline value is returned. // otherwise defaultValue is returned.
diff --git a/third_party/WebKit/Source/modules/webshare/NavigatorShare.cpp b/third_party/WebKit/Source/modules/webshare/NavigatorShare.cpp index 5216a08..84b82c2 100644 --- a/third_party/WebKit/Source/modules/webshare/NavigatorShare.cpp +++ b/third_party/WebKit/Source/modules/webshare/NavigatorShare.cpp
@@ -127,11 +127,6 @@ } void NavigatorShare::onConnectionError() { - if (!Platform::current()) { - // TODO(rockot): Clean this up once renderer shutdown sequence is fixed. - return; - } - for (auto& client : m_clients) { client->onConnectionError(); }
diff --git a/third_party/WebKit/Source/modules/webusb/USB.cpp b/third_party/WebKit/Source/modules/webusb/USB.cpp index be32f16..5e23731 100644 --- a/third_party/WebKit/Source/modules/webusb/USB.cpp +++ b/third_party/WebKit/Source/modules/webusb/USB.cpp
@@ -224,11 +224,6 @@ } void USB::onDeviceManagerConnectionError() { - if (!Platform::current()) { - // TODO(rockot): Clean this up once renderer shutdown sequence is fixed. - return; - } - m_deviceManager.reset(); for (ScriptPromiseResolver* resolver : m_deviceManagerRequests) resolver->reject(DOMException::create(NotFoundError, kNoServiceError)); @@ -236,11 +231,6 @@ } void USB::onChooserServiceConnectionError() { - if (!Platform::current()) { - // TODO(rockot): Clean this up once renderer shutdown sequence is fixed. - return; - } - m_chooserService.reset(); for (ScriptPromiseResolver* resolver : m_chooserServiceRequests) resolver->reject(DOMException::create(NotFoundError, kNoServiceError));
diff --git a/third_party/WebKit/Source/modules/webusb/USBDevice.cpp b/third_party/WebKit/Source/modules/webusb/USBDevice.cpp index 81f2840..7ae24bf 100644 --- a/third_party/WebKit/Source/modules/webusb/USBDevice.cpp +++ b/third_party/WebKit/Source/modules/webusb/USBDevice.cpp
@@ -945,11 +945,6 @@ } void USBDevice::onConnectionError() { - if (!Platform::current()) { - // TODO(rockot): Clean this up once renderer shutdown sequence is fixed. - return; - } - m_device.reset(); m_opened = false; for (ScriptPromiseResolver* resolver : m_deviceRequests)
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in index 486853d..a8be4a1b 100644 --- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in +++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
@@ -119,7 +119,7 @@ GeometryInterfaces status=experimental, implied_by=CompositorWorker GetUserMedia status=stable GlobalCacheStorage status=stable -HeapCompaction status=experimental +HeapCompaction status=stable IDBObserver status=experimental IdleTimeSpellChecking ImageCapture status=experimental, origin_trial_feature_name=ImageCapture
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollableArea.h b/third_party/WebKit/Source/platform/scroll/ScrollableArea.h index 1748a50..3d6fced 100644 --- a/third_party/WebKit/Source/platform/scroll/ScrollableArea.h +++ b/third_party/WebKit/Source/platform/scroll/ScrollableArea.h
@@ -319,6 +319,8 @@ return ScrollBehaviorInstant; } + virtual bool isScrollable() const { return true; } + // TODO(bokan): FrameView::setScrollOffset uses updateScrollbars to scroll // which bails out early if its already in updateScrollbars, the effect being // that programmatic scrolls (i.e. setScrollOffset) are disabled when in
diff --git a/third_party/WebKit/Source/platform/testing/ImageDecodeBench.cpp b/third_party/WebKit/Source/platform/testing/ImageDecodeBench.cpp index f1521f2e..f193948 100644 --- a/third_party/WebKit/Source/platform/testing/ImageDecodeBench.cpp +++ b/third_party/WebKit/Source/platform/testing/ImageDecodeBench.cpp
@@ -38,7 +38,9 @@ typedef struct stat sttype; #endif -using namespace blink; +namespace blink { + +namespace { #if defined(_WIN32) @@ -246,6 +248,8 @@ return !decoder->failed(); } +} // namespace + int main(int argc, char* argv[]) { base::CommandLine::Init(argc, argv); @@ -342,3 +346,9 @@ printf("%f %f\n", totalTime, averageTime); return 0; } + +} // namespace blink + +int main(int argc, char* argv[]) { + return blink::main(argc, argv); +}
diff --git a/third_party/WebKit/Source/platform/text/CharacterPropertyDataGenerator.cpp b/third_party/WebKit/Source/platform/text/CharacterPropertyDataGenerator.cpp index 57d63588..d822a14 100644 --- a/third_party/WebKit/Source/platform/text/CharacterPropertyDataGenerator.cpp +++ b/third_party/WebKit/Source/platform/text/CharacterPropertyDataGenerator.cpp
@@ -17,12 +17,12 @@ #include <utrie2.h> #endif +namespace blink { + #if defined(USING_SYSTEM_ICU) static void generate(FILE*) {} #else -using namespace blink; - const UChar32 kMaxCodepoint = 0x10FFFF; #define ARRAY_LENGTH(a) (sizeof(a) / sizeof((a)[0])) @@ -119,13 +119,15 @@ } #endif +} // namespace blink + int main(int argc, char** argv) { // Write the serialized array to the source file. if (argc <= 1) { - generate(stdout); + blink::generate(stdout); } else { FILE* fp = fopen(argv[1], "wb"); - generate(fp); + blink::generate(fp); fclose(fp); }
diff --git a/third_party/WebKit/Source/platform/threading/BackgroundTaskRunnerTest.cpp b/third_party/WebKit/Source/platform/threading/BackgroundTaskRunnerTest.cpp index 7bf55cb..5b3cc3d 100644 --- a/third_party/WebKit/Source/platform/threading/BackgroundTaskRunnerTest.cpp +++ b/third_party/WebKit/Source/platform/threading/BackgroundTaskRunnerTest.cpp
@@ -11,9 +11,9 @@ #include "wtf/PtrUtil.h" #include <memory> -namespace { +namespace blink { -using namespace blink; +namespace { void PingPongTask(WaitableEvent* doneEvent) { doneEvent->signal(); @@ -21,6 +21,8 @@ class BackgroundTaskRunnerTest : public testing::Test {}; +} // namespace + TEST_F(BackgroundTaskRunnerTest, RunShortTaskOnBackgroundThread) { std::unique_ptr<WaitableEvent> doneEvent = WTF::makeUnique<WaitableEvent>(); BackgroundTaskRunner::postOnBackgroundThread( @@ -41,4 +43,4 @@ doneEvent->wait(); } -} // unnamed namespace +} // namespace blink
diff --git a/third_party/WebKit/Source/platform/transforms/TranslateTransformOperation.cpp b/third_party/WebKit/Source/platform/transforms/TranslateTransformOperation.cpp index a7d45917..db85a54 100644 --- a/third_party/WebKit/Source/platform/transforms/TranslateTransformOperation.cpp +++ b/third_party/WebKit/Source/platform/transforms/TranslateTransformOperation.cpp
@@ -31,11 +31,12 @@ return this; const Length zeroLength(0, Fixed); - if (blendToIdentity) + if (blendToIdentity) { return TranslateTransformOperation::create( zeroLength.blend(m_x, progress, ValueRangeAll), zeroLength.blend(m_y, progress, ValueRangeAll), - blink::blend(0., m_z, progress), m_type); + blink::blend(m_z, 0., progress), m_type); + } const TranslateTransformOperation* fromOp = static_cast<const TranslateTransformOperation*>(from);
diff --git a/third_party/WebKit/Source/web/AssertMatchingEnums.cpp b/third_party/WebKit/Source/web/AssertMatchingEnums.cpp index 3518f80..959b73f 100644 --- a/third_party/WebKit/Source/web/AssertMatchingEnums.cpp +++ b/third_party/WebKit/Source/web/AssertMatchingEnums.cpp
@@ -413,10 +413,18 @@ STATIC_ASSERT_ENUM(WebAXTextAffinityUpstream, TextAffinity::Upstream); STATIC_ASSERT_ENUM(WebAXTextAffinityDownstream, TextAffinity::Downstream); +STATIC_ASSERT_ENUM(WebAXStringAttribute::AriaKeyShortcuts, + AXStringAttribute::AriaKeyShortcuts); +STATIC_ASSERT_ENUM(WebAXStringAttribute::AriaRoleDescription, + AXStringAttribute::AriaRoleDescription); STATIC_ASSERT_ENUM(WebAXObjectAttribute::AriaActiveDescendant, AXObjectAttribute::AriaActiveDescendant); +STATIC_ASSERT_ENUM(WebAXObjectAttribute::AriaErrorMessage, + AXObjectAttribute::AriaErrorMessage); STATIC_ASSERT_ENUM(WebAXObjectVectorAttribute::AriaControls, AXObjectVectorAttribute::AriaControls); +STATIC_ASSERT_ENUM(WebAXObjectVectorAttribute::AriaDetails, + AXObjectVectorAttribute::AriaDetails); STATIC_ASSERT_ENUM(WebAXObjectVectorAttribute::AriaFlowTo, AXObjectVectorAttribute::AriaFlowTo);
diff --git a/third_party/WebKit/Source/web/tests/IntersectionObserverTest.cpp b/third_party/WebKit/Source/web/tests/IntersectionObserverTest.cpp index 825eddd..fd14a1e 100644 --- a/third_party/WebKit/Source/web/tests/IntersectionObserverTest.cpp +++ b/third_party/WebKit/Source/web/tests/IntersectionObserverTest.cpp
@@ -91,16 +91,16 @@ compositor().beginFrame(); testing::runPendingTasks(); - EXPECT_EQ(observerCallback->callCount(), 0); + EXPECT_EQ(observerCallback->callCount(), 1); // When document is not suspended, beginFrame() will generate notifications // and post a task to deliver them. document().view()->layoutViewportScrollableArea()->setScrollOffset( ScrollOffset(0, 300), ProgrammaticScroll); compositor().beginFrame(); - EXPECT_EQ(observerCallback->callCount(), 0); - testing::runPendingTasks(); EXPECT_EQ(observerCallback->callCount(), 1); + testing::runPendingTasks(); + EXPECT_EQ(observerCallback->callCount(), 2); // When a document is suspended, beginFrame() will generate a notification, // but it will not be delivered. The notification will, however, be @@ -109,9 +109,9 @@ document().view()->layoutViewportScrollableArea()->setScrollOffset( ScrollOffset(0, 0), ProgrammaticScroll); compositor().beginFrame(); - EXPECT_EQ(observerCallback->callCount(), 1); + EXPECT_EQ(observerCallback->callCount(), 2); testing::runPendingTasks(); - EXPECT_EQ(observerCallback->callCount(), 1); + EXPECT_EQ(observerCallback->callCount(), 2); EXPECT_FALSE(observer->takeRecords(exceptionState).isEmpty()); // Generate a notification while document is suspended; then resume document. @@ -120,11 +120,11 @@ ScrollOffset(0, 300), ProgrammaticScroll); compositor().beginFrame(); testing::runPendingTasks(); - EXPECT_EQ(observerCallback->callCount(), 1); - document().resumeScheduledTasks(); - EXPECT_EQ(observerCallback->callCount(), 1); - testing::runPendingTasks(); EXPECT_EQ(observerCallback->callCount(), 2); + document().resumeScheduledTasks(); + EXPECT_EQ(observerCallback->callCount(), 2); + testing::runPendingTasks(); + EXPECT_EQ(observerCallback->callCount(), 3); } } // namespace blink
diff --git a/third_party/WebKit/Source/web/tests/PrerenderingTest.cpp b/third_party/WebKit/Source/web/tests/PrerenderingTest.cpp index 5ca09d2..37febc0 100644 --- a/third_party/WebKit/Source/web/tests/PrerenderingTest.cpp +++ b/third_party/WebKit/Source/web/tests/PrerenderingTest.cpp
@@ -49,13 +49,12 @@ #include <list> #include <memory> -using namespace blink; -using blink::URLTestHelpers::toKURL; +namespace blink { namespace { WebURL toWebURL(const char* url) { - return WebURL(toKURL(url)); + return WebURL(blink::URLTestHelpers::toKURL(url)); } class TestPrerendererClient : public WebPrerendererClient { @@ -226,6 +225,8 @@ FrameTestHelpers::WebViewHelper m_webViewHelper; }; +} // namespace + TEST_F(PrerenderingTest, SinglePrerender) { initialize("http://www.foo.com/", "prerender/single_prerender.html"); @@ -470,4 +471,4 @@ relNextAndPrerender.relTypes()); } -} // namespace +} // namespace blink
diff --git a/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp b/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp index 9f48095..3070cc1 100644 --- a/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp +++ b/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp
@@ -1646,13 +1646,15 @@ navigateTo(m_baseURL + "content-width-1000.html"); FrameView& frameView = *webViewImpl()->mainFrameImpl()->frameView(); - WebLayer* scrollLayer = frameView.layerForScrolling()->platformLayer(); webViewImpl()->mainFrame()->executeScript( WebScriptSource("var content = document.getElementById(\"content\");" "content.style.width = \"1500px\";" "content.style.height = \"2400px\";")); frameView.updateAllLifecyclePhases(); + WebLayer* scrollLayer = frameView.layoutViewportScrollableArea() + ->layerForScrolling() + ->platformLayer(); EXPECT_SIZE_EQ(IntSize(1500, 2400), IntSize(scrollLayer->bounds())); }
diff --git a/third_party/WebKit/public/platform/WebString.h b/third_party/WebKit/public/platform/WebString.h index c8cbf0f0..5272d3f 100644 --- a/third_party/WebKit/public/platform/WebString.h +++ b/third_party/WebKit/public/platform/WebString.h
@@ -184,39 +184,6 @@ BLINK_COMMON_EXPORT WebString(const WTF::AtomicString&); BLINK_COMMON_EXPORT WebString& operator=(const WTF::AtomicString&); BLINK_COMMON_EXPORT operator WTF::AtomicString() const; -#else - // WARNING: implicit conversions to/from string16 are being deprecated, - // use fromUTF16() or utf16() instead in new changes. - WebString(const base::string16& s) { assign(s.data(), s.length()); } - - WebString& operator=(const base::string16& s) { - assign(s.data(), s.length()); - return *this; - } - - operator base::string16() const { - return base::Latin1OrUTF16ToUTF16(length(), data8(), data16()); - } - - WebString(const base::NullableString16& s) { - if (s.is_null()) - reset(); - else - assign(s.string().data(), s.string().length()); - } - - WebString& operator=(const base::NullableString16& s) { - if (s.is_null()) - reset(); - else - assign(s.string().data(), s.string().length()); - return *this; - } - - operator base::NullableString16() const { - return base::NullableString16(operator base::string16(), - m_private.isNull()); - } #endif private:
diff --git a/third_party/WebKit/public/web/WebAXEnums.h b/third_party/WebKit/public/web/WebAXEnums.h index 1b72d74..2663a82 100644 --- a/third_party/WebKit/public/web/WebAXEnums.h +++ b/third_party/WebKit/public/web/WebAXEnums.h
@@ -360,13 +360,17 @@ // Sparse attributes of a WebAXObject whose value is a string. // In order for it to be a sparse attribute the default value // must be "". -enum class WebAXStringAttribute {}; +enum class WebAXStringAttribute { + AriaKeyShortcuts, + AriaRoleDescription, +}; // Sparse attributes of a WebAXObject whose value is a reference to // another WebAXObject within the same frame. In order for it to be a // sparse attribute the default value must be the null WebAXObject. enum class WebAXObjectAttribute { AriaActiveDescendant, + AriaErrorMessage, }; // Sparse attributes of a WebAXObject whose value is a vector of @@ -375,6 +379,7 @@ // empty vector. enum class WebAXObjectVectorAttribute { AriaControls, + AriaDetails, AriaFlowTo, };
diff --git a/third_party/gvr-android-sdk/display_synchronizer_jni.h b/third_party/gvr-android-sdk/display_synchronizer_jni.h index 89f1fc6..8b03dfc 100644 --- a/third_party/gvr-android-sdk/display_synchronizer_jni.h +++ b/third_party/gvr-android-sdk/display_synchronizer_jni.h
@@ -111,7 +111,7 @@ }; static bool RegisterNativesImpl(JNIEnv* env) { - if (base::android::IsManualJniRegistrationDisabled()) + if (jni_generator::ShouldSkipJniRegistration(false)) return true; const int kMethodsDisplaySynchronizerSize =
diff --git a/third_party/gvr-android-sdk/gvr_api_jni.h b/third_party/gvr-android-sdk/gvr_api_jni.h index 0c91a9a0..9d5a790 100644 --- a/third_party/gvr-android-sdk/gvr_api_jni.h +++ b/third_party/gvr-android-sdk/gvr_api_jni.h
@@ -1283,7 +1283,7 @@ }; static bool RegisterNativesImpl(JNIEnv* env) { - if (base::android::IsManualJniRegistrationDisabled()) + if (jni_generator::ShouldSkipJniRegistration(false)) return true; const int kMethodsGvrApiSize = arraysize(kMethodsGvrApi);
diff --git a/third_party/gvr-android-sdk/native_callbacks_jni.h b/third_party/gvr-android-sdk/native_callbacks_jni.h index 7bbec0a..e5469f5 100644 --- a/third_party/gvr-android-sdk/native_callbacks_jni.h +++ b/third_party/gvr-android-sdk/native_callbacks_jni.h
@@ -265,7 +265,7 @@ }; static bool RegisterNativesImpl(JNIEnv* env) { - if (base::android::IsManualJniRegistrationDisabled()) + if (jni_generator::ShouldSkipJniRegistration(false)) return true; const int kMethodsNativeCallbacksSize = arraysize(kMethodsNativeCallbacks);
diff --git a/tools/chrome_proxy/webdriver/video.py b/tools/chrome_proxy/webdriver/video.py new file mode 100644 index 0000000..a88022c --- /dev/null +++ b/tools/chrome_proxy/webdriver/video.py
@@ -0,0 +1,22 @@ +# 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. + +import common +from common import TestDriver +from common import IntegrationTest + + +class Video(IntegrationTest): + + # Check videos are proxied. + def testCheckVideoHasViaHeader(self): + with TestDriver() as t: + t.AddChromeArg('--enable-spdy-proxy-auth') + t.LoadURL( + 'http://check.googlezip.net/cacheable/video/buck_bunny_tiny.html') + for response in t.GetHTTPResponses(): + self.assertHasChromeProxyViaHeader(response) + +if __name__ == '__main__': + IntegrationTest.RunAllTests()
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index e14b164..7f458b6 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -85768,6 +85768,7 @@ <int value="359" label="Set the roaming profile directory."/> <int value="360" label="Configure the New Tab page URL."/> <int value="361" label="Maximum SSL version enabled"/> + <int value="362" label="Show Cast icon in the toolbar or the overflow menu."/> </enum> <enum name="EnterprisePolicyInvalidations" type="int"> @@ -95572,6 +95573,7 @@ <int value="-2143328006" label="enable-fill-on-account-select-no-highlighting"/> <int value="-2143113994" label="enable-ephemeral-apps-in-webstore"/> + <int value="-2134333982" label="ShowArcFilesApp:enabled"/> <int value="-2134244069" label="HttpFormWarning:enabled"/> <int value="-2132591642" label="enable-input-view"/> <int value="-2122048316" @@ -95829,6 +95831,7 @@ <int value="-1077752943" label="enable-password-generation"/> <int value="-1075156797" label="enable-brotli"/> <int value="-1075089382" label="enable-physical-web"/> + <int value="-1073479583" label="ShowArcFilesApp:disabled"/> <int value="-1062119671" label="enable-password-force-saving"/> <int value="-1056310158" label="disable-memory-pressure-chromeos"/> <int value="-1052782474" label="enable-cloud-devices"/>
diff --git a/tools/perf/benchmarks/blink_perf.py b/tools/perf/benchmarks/blink_perf.py index 8cc2d3f..ce33a8fa 100644 --- a/tools/perf/benchmarks/blink_perf.py +++ b/tools/perf/benchmarks/blink_perf.py
@@ -97,9 +97,9 @@ options.AppendExtraBrowserArgs('--expose-internals-for-testing') def ValidateAndMeasurePage(self, page, tab, results): - tab.WaitForJavaScriptExpression('testRunner.isDone', 600) + tab.WaitForJavaScriptCondition2('testRunner.isDone', timeout=600) - log = tab.EvaluateJavaScript('document.getElementById("log").innerHTML') + log = tab.EvaluateJavaScript2('document.getElementById("log").innerHTML') for line in log.splitlines(): if line.startswith("FATAL: "):
diff --git a/tools/perf/benchmarks/dromaeo.py b/tools/perf/benchmarks/dromaeo.py index 7cbcaa9..22289d0 100644 --- a/tools/perf/benchmarks/dromaeo.py +++ b/tools/perf/benchmarks/dromaeo.py
@@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import json import math import os @@ -32,36 +33,36 @@ self._power_metric.Start(page, tab) def ValidateAndMeasurePage(self, page, tab, results): - tab.WaitForJavaScriptExpression( + tab.WaitForJavaScriptCondition2( 'window.document.getElementById("pause") &&' + 'window.document.getElementById("pause").value == "Run"', - 120) + timeout=120) # Start spying on POST request that will report benchmark results, and # intercept result data. - tab.ExecuteJavaScript('(function() {' + - ' var real_jquery_ajax_ = window.jQuery;' + - ' window.results_ = "";' + - ' window.jQuery.ajax = function(request) {' + - ' if (request.url == "store.php") {' + - ' window.results_ =' + - ' decodeURIComponent(request.data);' + - ' window.results_ = window.results_.substring(' + - ' window.results_.indexOf("=") + 1, ' + - ' window.results_.lastIndexOf("&"));' + - ' real_jquery_ajax_(request);' + - ' }' + - ' };' + - '})();') + tab.ExecuteJavaScript2(""" + (function() { + var real_jquery_ajax_ = window.jQuery; + window.results_ = ""; + window.jQuery.ajax = function(request) { + if (request.url == "store.php") { + window.results_ = decodeURIComponent(request.data); + window.results_ = window.results_.substring( + window.results_.indexOf("=") + 1, + window.results_.lastIndexOf("&")); + real_jquery_ajax_(request); + } + }; + })();""") # Starts benchmark. - tab.ExecuteJavaScript('window.document.getElementById("pause").click();') + tab.ExecuteJavaScript2('window.document.getElementById("pause").click();') - tab.WaitForJavaScriptExpression('!!window.results_', 600) + tab.WaitForJavaScriptCondition2('!!window.results_', timeout=600) self._power_metric.Stop(page, tab) self._power_metric.AddResults(tab, results) - score = eval(tab.EvaluateJavaScript('window.results_ || "[]"')) + score = json.loads(tab.EvaluateJavaScript2('window.results_ || "[]"')) def Escape(k): chars = [' ', '.', '-', '/', '(', ')', '*']
diff --git a/tools/perf/benchmarks/indexeddb_perf.py b/tools/perf/benchmarks/indexeddb_perf.py index 69723ab7..41847ca 100644 --- a/tools/perf/benchmarks/indexeddb_perf.py +++ b/tools/perf/benchmarks/indexeddb_perf.py
@@ -65,7 +65,7 @@ def ValidateAndMeasurePage(self, page, tab, results): tab.WaitForDocumentReadyStateToBeComplete() - tab.WaitForJavaScriptExpression('window.done', 600) + tab.WaitForJavaScriptCondition2('window.done', timeout=600) self._power_metric.Stop(page, tab) self._memory_metric.Stop(page, tab) @@ -73,8 +73,8 @@ self._memory_metric.AddResults(tab, results) self._power_metric.AddResults(tab, results) - js_get_results = 'JSON.stringify(automation.getResults());' - result_dict = json.loads(tab.EvaluateJavaScript(js_get_results)) + result_dict = json.loads(tab.EvaluateJavaScript2( + 'JSON.stringify(automation.getResults());')) total = 0.0 for key in result_dict: if key == 'OverallTestDuration':
diff --git a/tools/perf/benchmarks/jetstream.py b/tools/perf/benchmarks/jetstream.py index 7be2f9a..ec1be5f 100644 --- a/tools/perf/benchmarks/jetstream.py +++ b/tools/perf/benchmarks/jetstream.py
@@ -47,20 +47,16 @@ def ValidateAndMeasurePage(self, page, tab, results): del page # unused - get_results_js = """ + tab.WaitForDocumentReadyStateToBeComplete() + tab.EvaluateJavaScript2('JetStream.start()') + result = tab.WaitForJavaScriptCondition2(""" (function() { for (var i = 0; i < __results.length; i++) { if (!__results[i].indexOf('Raw results: ')) return __results[i]; } return null; })(); - """ - - tab.WaitForDocumentReadyStateToBeComplete() - tab.EvaluateJavaScript('JetStream.start()') - tab.WaitForJavaScriptExpression(get_results_js, 600) - - result = tab.EvaluateJavaScript(get_results_js) + """, timeout=600) result = json.loads(result.partition(': ')[2]) all_score_lists = []
diff --git a/tools/perf/benchmarks/kraken.py b/tools/perf/benchmarks/kraken.py index c2322ee7..d2834b5c6 100644 --- a/tools/perf/benchmarks/kraken.py +++ b/tools/perf/benchmarks/kraken.py
@@ -4,6 +4,7 @@ """Runs Mozilla's Kraken JavaScript benchmark.""" +import json import os from core import perf_benchmark @@ -85,18 +86,17 @@ self._power_metric.Start(page, tab) def ValidateAndMeasurePage(self, page, tab, results): - tab.WaitForJavaScriptExpression( - 'document.title.indexOf("Results") != -1', 700) + tab.WaitForJavaScriptCondition2( + 'document.title.indexOf("Results") != -1', timeout=700) tab.WaitForDocumentReadyStateToBeComplete() self._power_metric.Stop(page, tab) self._power_metric.AddResults(tab, results) - js_get_results = """ + result_dict = json.loads(tab.EvaluateJavaScript2(""" var formElement = document.getElementsByTagName("input")[0]; decodeURIComponent(formElement.value.split("?")[1]); - """ - result_dict = eval(tab.EvaluateJavaScript(js_get_results)) + """)) total = 0 for key in result_dict: if key == 'v':
diff --git a/tools/perf/benchmarks/media.py b/tools/perf/benchmarks/media.py index b1d68e92..5c6faf38 100644 --- a/tools/perf/benchmarks/media.py +++ b/tools/perf/benchmarks/media.py
@@ -20,7 +20,7 @@ def ValidateAndMeasurePage(self, page, tab, results): del page # unused - media_metric = tab.EvaluateJavaScript('window.__testMetrics') + media_metric = tab.EvaluateJavaScript2('window.__testMetrics') trace = media_metric['id'] if 'id' in media_metric else None metrics = media_metric['metrics'] if 'metrics' in media_metric else [] for m in metrics:
diff --git a/tools/perf/benchmarks/memory.py b/tools/perf/benchmarks/memory.py index 28c30faa..bbd54076 100644 --- a/tools/perf/benchmarks/memory.py +++ b/tools/perf/benchmarks/memory.py
@@ -78,6 +78,7 @@ return not _IGNORED_STATS_RE.search(value.name) +@benchmark.Disabled('win', 'mac', 'linux') # https://crbug.com/687258 class MemoryBenchmarkTop10MobileStress(MemoryBenchmarkTop10Mobile): """Run top 10 mobile page set without closing/restarting the browser.
diff --git a/tools/perf/benchmarks/octane.py b/tools/perf/benchmarks/octane.py index 02fb9bf4..98ec3da7 100644 --- a/tools/perf/benchmarks/octane.py +++ b/tools/perf/benchmarks/octane.py
@@ -99,14 +99,14 @@ self._power_metric.Start(page, tab) def ValidateAndMeasurePage(self, page, tab, results): - tab.WaitForJavaScriptExpression('window.completed', 10) - tab.WaitForJavaScriptExpression( - '!document.getElementById("progress-bar-container")', 1200) + tab.WaitForJavaScriptCondition2('window.completed', timeout=10) + tab.WaitForJavaScriptCondition2( + '!document.getElementById("progress-bar-container")', timeout=1200) self._power_metric.Stop(page, tab) self._power_metric.AddResults(tab, results) - results_log = tab.EvaluateJavaScript('__results') + results_log = tab.EvaluateJavaScript2('__results') all_scores = [] for output in results_log: # Split the results into score and test name.
diff --git a/tools/perf/benchmarks/oortonline.py b/tools/perf/benchmarks/oortonline.py index e7c4317..13fc7f9e 100644 --- a/tools/perf/benchmarks/oortonline.py +++ b/tools/perf/benchmarks/oortonline.py
@@ -23,8 +23,8 @@ def ValidateAndMeasurePage(self, page, tab, results): del page # unused - tab.WaitForJavaScriptExpression('window.benchmarkFinished', 1000) - scores = tab.EvaluateJavaScript('window.benchmarkScore') + tab.WaitForJavaScriptCondition2('window.benchmarkFinished', timeout=1000) + scores = tab.EvaluateJavaScript2('window.benchmarkScore') for score in scores: valid = score['valid'] if valid:
diff --git a/tools/perf/benchmarks/robohornet_pro.py b/tools/perf/benchmarks/robohornet_pro.py index 7a0e22a86e..dfa54b2 100644 --- a/tools/perf/benchmarks/robohornet_pro.py +++ b/tools/perf/benchmarks/robohornet_pro.py
@@ -33,15 +33,15 @@ self._power_metric.Start(page, tab) def ValidateAndMeasurePage(self, page, tab, results): - tab.ExecuteJavaScript('ToggleRoboHornet()') - tab.WaitForJavaScriptExpression( + tab.ExecuteJavaScript2('ToggleRoboHornet()') + tab.WaitForJavaScriptCondition2( 'document.getElementById("results").innerHTML.indexOf("Total") != -1', - 600) + timeout=600) self._power_metric.Stop(page, tab) self._power_metric.AddResults(tab, results) - result = int(tab.EvaluateJavaScript('stopTime - startTime')) + result = int(tab.EvaluateJavaScript2('stopTime - startTime')) results.AddValue( scalar.ScalarValue(results.current_page, 'Total', 'ms', result))
diff --git a/tools/perf/benchmarks/service_worker.py b/tools/perf/benchmarks/service_worker.py index 71d6f2c..f9f12502 100644 --- a/tools/perf/benchmarks/service_worker.py +++ b/tools/perf/benchmarks/service_worker.py
@@ -151,8 +151,8 @@ def ValidateAndMeasurePage(self, page, tab, results): del page # unused - tab.WaitForJavaScriptExpression('window.done', 40) - json = tab.EvaluateJavaScript('window.results || {}') + tab.WaitForJavaScriptCondition2('window.done', timeout=40) + json = tab.EvaluateJavaScript2('window.results || {}') for key, value in json.iteritems(): results.AddValue(scalar.ScalarValue( results.current_page, key, value['units'], value['value']))
diff --git a/tools/perf/benchmarks/spaceport.py b/tools/perf/benchmarks/spaceport.py index 66e0678..5d13cb9 100644 --- a/tools/perf/benchmarks/spaceport.py +++ b/tools/perf/benchmarks/spaceport.py
@@ -4,6 +4,7 @@ """Runs spaceport.io's PerfMarks benchmark.""" +import json import logging import os @@ -59,10 +60,11 @@ def ValidateAndMeasurePage(self, page, tab, results): del page # unused - tab.WaitForJavaScriptExpression( - '!document.getElementById("start-performance-tests").disabled', 60) + tab.WaitForJavaScriptCondition2( + '!document.getElementById("start-performance-tests").disabled', + timeout=60) - tab.ExecuteJavaScript(""" + tab.ExecuteJavaScript2(""" window.__results = {}; window.console.log = function(str) { if (!str) return; @@ -76,15 +78,16 @@ num_results = 0 num_tests_in_spaceport = 24 while num_results < num_tests_in_spaceport: - # TODO(catapult:#3028): Fix interpolation of JavaScript values. - tab.WaitForJavaScriptExpression( - 'Object.keys(window.__results).length > %d' % num_results, 180) - num_results = tab.EvaluateJavaScript( + tab.WaitForJavaScriptCondition2( + 'Object.keys(window.__results).length > {{ num_results }}', + num_results=num_results, + timeout=180) + num_results = tab.EvaluateJavaScript2( 'Object.keys(window.__results).length') logging.info('Completed test %d of %d', num_results, num_tests_in_spaceport) - result_dict = eval(tab.EvaluateJavaScript( + result_dict = json.loads(tab.EvaluateJavaScript2( 'JSON.stringify(window.__results)')) for key in result_dict: chart, trace = key.split('.', 1)
diff --git a/tools/perf/benchmarks/speedometer.py b/tools/perf/benchmarks/speedometer.py index 282b747..c232804 100644 --- a/tools/perf/benchmarks/speedometer.py +++ b/tools/perf/benchmarks/speedometer.py
@@ -56,36 +56,38 @@ if tab.browser.platform.GetOSName() == 'android': iterationCount = 3 - # TODO(catapult:#3028): Fix interpolation of JavaScript values. - tab.ExecuteJavaScript(""" + tab.ExecuteJavaScript2(""" // Store all the results in the benchmarkClient benchmarkClient._measuredValues = [] benchmarkClient.didRunSuites = function(measuredValues) { benchmarkClient._measuredValues.push(measuredValues); benchmarkClient._timeValues.push(measuredValues.total); }; - benchmarkClient.iterationCount = %d; + benchmarkClient.iterationCount = {{ count }}; startTest(); - """ % iterationCount) - tab.WaitForJavaScriptExpression( - 'benchmarkClient._finishedTestCount == benchmarkClient.testsCount', 600) + """, + count=iterationCount) + tab.WaitForJavaScriptCondition2( + 'benchmarkClient._finishedTestCount == benchmarkClient.testsCount', + timemout=600) results.AddValue(list_of_scalar_values.ListOfScalarValues( page, 'Total', 'ms', - tab.EvaluateJavaScript('benchmarkClient._timeValues'), important=True)) + tab.EvaluateJavaScript2('benchmarkClient._timeValues'), + important=True)) # Extract the timings for each suite for suite_name in self.enabled_suites: results.AddValue(list_of_scalar_values.ListOfScalarValues( page, suite_name, 'ms', - # TODO(catapult:#3028): Fix interpolation of JavaScript values. - tab.EvaluateJavaScript(""" + tab.EvaluateJavaScript2(""" var suite_times = []; for(var i = 0; i < benchmarkClient.iterationCount; i++) { suite_times.push( - benchmarkClient._measuredValues[i].tests['%s'].total); + benchmarkClient._measuredValues[i].tests[{{ key }}].total); }; suite_times; - """ % suite_name), important=False)) + """, + key=suite_name), important=False)) keychain_metric.KeychainMetric().AddResults(tab, results)
diff --git a/tools/perf/benchmarks/sunspider.py b/tools/perf/benchmarks/sunspider.py index 507751f..a504426d 100644 --- a/tools/perf/benchmarks/sunspider.py +++ b/tools/perf/benchmarks/sunspider.py
@@ -91,15 +91,14 @@ self._power_metric.Start(page, tab) def ValidateAndMeasurePage(self, page, tab, results): - tab.WaitForJavaScriptExpression( + tab.WaitForJavaScriptCondition2( 'window.location.pathname.indexOf("results.html") >= 0' - '&& typeof(output) != "undefined"', 300) + '&& typeof(output) != "undefined"', timeout=300) self._power_metric.Stop(page, tab) self._power_metric.AddResults(tab, results) - js_get_results = 'JSON.stringify(output);' - js_results = json.loads(tab.EvaluateJavaScript(js_get_results)) + js_results = json.loads(tab.EvaluateJavaScript2('JSON.stringify(output);')) # Below, r is a map of benchmark names to lists of result numbers, # and totals is a list of totals of result numbers.
diff --git a/tools/perf/core/trybot_command.py b/tools/perf/core/trybot_command.py index f2a139e1..c533b03 100644 --- a/tools/perf/core/trybot_command.py +++ b/tools/perf/core/trybot_command.py
@@ -182,7 +182,7 @@ if returncode: if ignore_return_code: return None - raise TrybotError('%s. \n%s' % (msg_on_error, err)) + raise TrybotError('%s. \n%s \n%s' % (msg_on_error, err, output)) return output.strip()
diff --git a/tools/perf/measurements/blink_style.py b/tools/perf/measurements/blink_style.py index dae97c7..4e31a9ed 100644 --- a/tools/perf/measurements/blink_style.py +++ b/tools/perf/measurements/blink_style.py
@@ -30,7 +30,7 @@ def ValidateAndMeasurePage(self, page, tab, results): with tab.action_runner.CreateInteraction('wait-for-quiescence'): - tab.ExecuteJavaScript('console.time("");') + tab.ExecuteJavaScript2('console.time("");') try: util.WaitFor(tab.HasReachedQuiescence, 15) except py_utils.TimeoutException: @@ -39,7 +39,7 @@ # state on every run. pass - tab.ExecuteJavaScript(''' + tab.ExecuteJavaScript2(''' for (var i = 0; i < 11; i++) { var cold = i % 2 == 0; var name = "update_style";
diff --git a/tools/perf/measurements/image_decoding.py b/tools/perf/measurements/image_decoding.py index 6ce15e9..bde481cd 100644 --- a/tools/perf/measurements/image_decoding.py +++ b/tools/perf/measurements/image_decoding.py
@@ -24,13 +24,13 @@ self._power_metric = power.PowerMetric(platform) def WillNavigateToPage(self, page, tab): - tab.ExecuteJavaScript(""" + tab.ExecuteJavaScript2(""" if (window.chrome && chrome.gpuBenchmarking && chrome.gpuBenchmarking.clearImageCache) { chrome.gpuBenchmarking.clearImageCache(); } - """) + """) self._power_metric.Start(page, tab) config = tracing_config.TracingConfig() @@ -49,11 +49,11 @@ def StopBrowserAfterPage(self, browser, page): del page # unused - return not browser.tabs[0].ExecuteJavaScript(""" + return not browser.tabs[0].ExecuteJavaScript2(""" window.chrome && chrome.gpuBenchmarking && chrome.gpuBenchmarking.clearImageCache; - """) + """) def ValidateAndMeasurePage(self, page, tab, results): timeline_data = tab.browser.platform.tracing_controller.StopTracing() @@ -62,7 +62,7 @@ self._power_metric.AddResults(tab, results) def _IsDone(): - return tab.EvaluateJavaScript('isDone') + return tab.EvaluateJavaScript2('isDone') decode_image_events = timeline_model.GetAllEventsOfName( 'ImageFrameGenerator::decode') @@ -72,13 +72,12 @@ # If it is a real image page, then store only the last-minIterations # decode tasks. - if ( - hasattr( + if (hasattr( page, 'image_decoding_measurement_limit_results_to_min_iterations') and page.image_decoding_measurement_limit_results_to_min_iterations): assert _IsDone() - min_iterations = tab.EvaluateJavaScript('minIterations') + min_iterations = tab.EvaluateJavaScript2('minIterations') decode_image_events = decode_image_events[-min_iterations:] durations = [d.duration for d in decode_image_events] @@ -92,7 +91,7 @@ 'located at chrome/test/data/image_decoding.')) results.AddValue(scalar.ScalarValue( results.current_page, 'ImageLoading_avg', 'ms', - tab.EvaluateJavaScript('averageLoadingTimeMs()'))) + tab.EvaluateJavaScript2('averageLoadingTimeMs()'))) def DidRunPage(self, platform): self._power_metric.Close()
diff --git a/tools/perf/measurements/multipage_skpicture_printer.py b/tools/perf/measurements/multipage_skpicture_printer.py index 85e31b2e..c92d836 100644 --- a/tools/perf/measurements/multipage_skpicture_printer.py +++ b/tools/perf/measurements/multipage_skpicture_printer.py
@@ -6,9 +6,6 @@ from telemetry.page import legacy_page_test -_JS = 'chrome.gpuBenchmarking.printPagesToSkPictures("{0}");' - - class MultipageSkpicturePrinter(legacy_page_test.LegacyPageTest): def __init__(self, mskp_outdir): @@ -25,9 +22,10 @@ raise legacy_page_test.MeasurementFailure( 'Multipage SkPicture printing not supported on this platform') - # Replace win32 path separator char '\' with '\\'. outpath = os.path.abspath( os.path.join(self._mskp_outdir, page.file_safe_name + '.mskp')) - # TODO(catapult:#3028): Fix interpolation of JavaScript values. - js = _JS.format(outpath.replace('\\', '\\\\')) - tab.EvaluateJavaScript(js) + # Replace win32 path separator char '\' with '\\'. + outpath = outpath.replace('\\', '\\\\') + tab.EvaluateJavaScript2( + 'chrome.gpuBenchmarking.printPagesToSkPictures({{ outpath }});', + outpath=outpath)
diff --git a/tools/perf/measurements/oilpan_gc_times.py b/tools/perf/measurements/oilpan_gc_times.py index 774be256..7ab0de24 100644 --- a/tools/perf/measurements/oilpan_gc_times.py +++ b/tools/perf/measurements/oilpan_gc_times.py
@@ -188,7 +188,7 @@ super(OilpanGCTimesForBlinkPerf, self).WillNavigateToPage(page, tab) def ValidateAndMeasurePage(self, page, tab, results): - tab.WaitForJavaScriptExpression('testRunner.isDone', 600) + tab.WaitForJavaScriptCondition2('testRunner.isDone', timeout=600) super(OilpanGCTimesForBlinkPerf, self).ValidateAndMeasurePage( page, tab, results)
diff --git a/tools/perf/measurements/rasterize_and_record_micro.py b/tools/perf/measurements/rasterize_and_record_micro.py index 270f528..ecf5cf7d 100644 --- a/tools/perf/measurements/rasterize_and_record_micro.py +++ b/tools/perf/measurements/rasterize_and_record_micro.py
@@ -35,8 +35,7 @@ time.sleep(self._start_wait_time) # Enqueue benchmark - # TODO(catapult:#3028): Fix interpolation of JavaScript values. - tab.ExecuteJavaScript(""" + tab.ExecuteJavaScript2(""" window.benchmark_results = {}; window.benchmark_results.done = false; window.benchmark_results.id = @@ -46,20 +45,22 @@ window.benchmark_results.done = true; window.benchmark_results.results = value; }, { - "record_repeat_count": %i, - "rasterize_repeat_count": %i + "record_repeat_count": {{ record_repeat_count }}, + "rasterize_repeat_count": {{ rasterize_repeat_count }} }); - """ % (self._record_repeat, self._rasterize_repeat)) + """, + record_repeat_count=self._record_repeat, + rasterize_repeat_count=self._rasterize_repeat) - benchmark_id = tab.EvaluateJavaScript('window.benchmark_results.id') + benchmark_id = tab.EvaluateJavaScript2('window.benchmark_results.id') if not benchmark_id: raise legacy_page_test.MeasurementFailure( 'Failed to schedule rasterize_and_record_micro') - tab.WaitForJavaScriptExpression( - 'window.benchmark_results.done', self._timeout) + tab.WaitForJavaScriptCondition2( + 'window.benchmark_results.done', timeout=self._timeout) - data = tab.EvaluateJavaScript('window.benchmark_results.results') + data = tab.EvaluateJavaScript2('window.benchmark_results.results') pixels_recorded = data['pixels_recorded'] record_time = data['record_time_ms']
diff --git a/tools/perf/measurements/skpicture_printer.py b/tools/perf/measurements/skpicture_printer.py index 0890fc0..f85562a 100644 --- a/tools/perf/measurements/skpicture_printer.py +++ b/tools/perf/measurements/skpicture_printer.py
@@ -8,9 +8,6 @@ from telemetry.value import scalar -_JS = 'chrome.gpuBenchmarking.printToSkPicture("{0}");' - - class SkpicturePrinter(legacy_page_test.LegacyPageTest): def __init__(self, skp_outdir): @@ -27,12 +24,13 @@ raise legacy_page_test.MeasurementFailure( 'SkPicture printing not supported on this platform') - # Replace win32 path separator char '\' with '\\'. outpath = os.path.abspath( os.path.join(self._skp_outdir, page.file_safe_name)) - # TODO(catapult:#3028): Fix interpolation of JavaScript values. - js = _JS.format(outpath.replace('\\', '\\\\')) - tab.EvaluateJavaScript(js) + # Replace win32 path separator char '\' with '\\'. + outpath = outpath.replace('\\', '\\\\') + tab.EvaluateJavaScript2( + 'chrome.gpuBenchmarking.printToSkPicture({{ outpath }});', + outpath=outpath) pictures = glob.glob(os.path.join(outpath, '*.skp')) results.AddValue(scalar.ScalarValue( results.current_page, 'saved_picture_count', 'count', len(pictures)))
diff --git a/tools/perf/metrics/loading.py b/tools/perf/metrics/loading.py index fa1ee3b..ac9b51f 100644 --- a/tools/perf/metrics/loading.py +++ b/tools/perf/metrics/loading.py
@@ -18,7 +18,7 @@ raise NotImplementedError() def AddResults(self, tab, results): - load_timings = tab.EvaluateJavaScript('window.performance.timing') + load_timings = tab.EvaluateJavaScript2('window.performance.timing') # NavigationStart relative markers in milliseconds. load_start = (
diff --git a/tools/perf/metrics/media.py b/tools/perf/metrics/media.py index 78411351..4e0519f 100644 --- a/tools/perf/metrics/media.py +++ b/tools/perf/metrics/media.py
@@ -23,7 +23,7 @@ super(MediaMetric, self).__init__() with open(os.path.join(os.path.dirname(__file__), 'media.js')) as f: js = f.read() - tab.ExecuteJavaScript(js) + tab.ExecuteJavaScript2(js) self._results = None self._skip_basic_metrics = False @@ -31,10 +31,10 @@ """Create the media metrics for all media elements in the document.""" if hasattr(page, 'skip_basic_metrics'): self._skip_basic_metrics = page.skip_basic_metrics - tab.ExecuteJavaScript('window.__createMediaMetricsForDocument()') + tab.ExecuteJavaScript2('window.__createMediaMetricsForDocument()') def Stop(self, page, tab): - self._results = tab.EvaluateJavaScript('window.__getAllMetrics()') + self._results = tab.EvaluateJavaScript2('window.__getAllMetrics()') # Optional |exclude_metrics| args are not in base class Metric. # pylint: disable=arguments-differ
diff --git a/tools/perf/metrics/startup_metric.py b/tools/perf/metrics/startup_metric.py index b58e1bbf..ff07b8b 100644 --- a/tools/perf/metrics/startup_metric.py +++ b/tools/perf/metrics/startup_metric.py
@@ -57,16 +57,16 @@ def RecordOneTab(t): def EvaluateInt(exp): - val = t.EvaluateJavaScript(exp) + val = t.EvaluateJavaScript2(exp) if not val: logging.warn('%s undefined', exp) return 0 return int(val) try: - t.WaitForJavaScriptExpression( + t.WaitForJavaScriptCondition2( 'window.performance.timing["loadEventEnd"] > 0', - self.DEFAULT_LOADING_TIMEOUT) + timeout=self.DEFAULT_LOADING_TIMEOUT) # EvaluateJavaScript(window.performance.timing) doesn't guarantee to # return the desired javascript object (crbug/472603). It may return an @@ -104,11 +104,10 @@ foreground_tab_stats.request_start_ms - browser_main_entry_time_ms)) def AddResults(self, tab, results): - get_histogram_js = 'statsCollectionController.getBrowserHistogram("%s")' - for display_name, histogram_name in self.HISTOGRAMS_TO_RECORD.iteritems(): - # TODO(catapult:#3028): Fix interpolation of JavaScript values. - result = tab.EvaluateJavaScript(get_histogram_js % histogram_name) + result = tab.EvaluateJavaScript2( + 'statsCollectionController.getBrowserHistogram({{ name }})', + name=histogram_name) result = json.loads(result) measured_time = 0
diff --git a/tools/perf/metrics/webrtc_stats.py b/tools/perf/metrics/webrtc_stats.py index 8c35703..6b64f5a4 100644 --- a/tools/perf/metrics/webrtc_stats.py +++ b/tools/perf/metrics/webrtc_stats.py
@@ -140,7 +140,7 @@ def Stop(self, page, tab): """Digs out stats from data populated by the javascript in webrtc_cases.""" - self._all_reports = tab.EvaluateJavaScript( + self._all_reports = tab.EvaluateJavaScript2( 'JSON.stringify(window.peerConnectionReports)') def AddResults(self, tab, results):
diff --git a/tools/perf/metrics/webrtc_stats_unittest.py b/tools/perf/metrics/webrtc_stats_unittest.py index b6f3488..79943c8 100644 --- a/tools/perf/metrics/webrtc_stats_unittest.py +++ b/tools/perf/metrics/webrtc_stats_unittest.py
@@ -113,7 +113,7 @@ stats_metric.Start(page, tab) - tab.ExpectCall('EvaluateJavaScript', + tab.ExpectCall('EvaluateJavaScript2', simple_mock.DONT_CARE).WillReturn(json_to_return) stats_metric.Stop(page, tab)
diff --git a/tools/perf/profile_creators/fast_navigation_profile_extender.py b/tools/perf/profile_creators/fast_navigation_profile_extender.py index a294dc8..318198d 100644 --- a/tools/perf/profile_creators/fast_navigation_profile_extender.py +++ b/tools/perf/profile_creators/fast_navigation_profile_extender.py
@@ -120,7 +120,7 @@ """Retrives the URL of the tab.""" # TODO(erikchen): Use tab.url instead, which talks to the browser process # instead of the renderer process. http://crbug.com/486119 - return tab.EvaluateJavaScript('document.URL', timeout) + return tab.EvaluateJavaScript2('document.URL', timeout=timeout) def _WaitForUrlToChange(self, tab, initial_url, end_time): """Waits for the tab to navigate away from its initial url.
diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc index c22d5117..d27aa9d 100644 --- a/ui/views/bubble/bubble_frame_view.cc +++ b/ui/views/bubble/bubble_frame_view.cc
@@ -25,9 +25,9 @@ #include "ui/resources/grit/ui_resources.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/bubble/bubble_border.h" -#include "ui/views/controls/button/label_button.h" #include "ui/views/controls/button/vector_icon_button.h" #include "ui/views/controls/image_view.h" +#include "ui/views/controls/label.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/layout_constants.h" #include "ui/views/resources/grit/views_resources.h" @@ -46,7 +46,7 @@ const SkColor kFootnoteBorderColor = SkColorSetRGB(229, 229, 229); constexpr int kClosePaddingRight = 7; -constexpr int kClosePaddingTop = 6; +constexpr int kClosePaddingTop = 7; // The MD spec states that the center of the "x" should be 16x16 from the top // right of the dialog. @@ -116,26 +116,25 @@ // static Button* BubbleFrameView::CreateCloseButton(VectorIconButtonDelegate* delegate) { - Button* close_button = nullptr; + ImageButton* close_button = nullptr; if (ui::MaterialDesignController::IsSecondaryUiMaterial()) { VectorIconButton* close = new VectorIconButton(delegate); close->SetIcon(gfx::VectorIconId::BAR_CLOSE); - close->SetSize(close->GetPreferredSize()); close_button = close; } else { - ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - LabelButton* close = new LabelButton(delegate, base::string16()); - close->SetImage(CustomButton::STATE_NORMAL, - *rb.GetImageNamed(IDR_CLOSE_DIALOG).ToImageSkia()); - close->SetImage(CustomButton::STATE_HOVERED, - *rb.GetImageNamed(IDR_CLOSE_DIALOG_H).ToImageSkia()); - close->SetImage(CustomButton::STATE_PRESSED, - *rb.GetImageNamed(IDR_CLOSE_DIALOG_P).ToImageSkia()); - close->SetBorder(nullptr); - close->SetSize(close->GetPreferredSize()); - close_button = close; + ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); + close_button = new ImageButton(delegate); + close_button->SetImage(CustomButton::STATE_NORMAL, + *rb->GetImageNamed(IDR_CLOSE_DIALOG).ToImageSkia()); + close_button->SetImage( + CustomButton::STATE_HOVERED, + *rb->GetImageNamed(IDR_CLOSE_DIALOG_H).ToImageSkia()); + close_button->SetImage( + CustomButton::STATE_PRESSED, + *rb->GetImageNamed(IDR_CLOSE_DIALOG_P).ToImageSkia()); } close_button->SetTooltipText(l10n_util::GetStringUTF16(IDS_APP_CLOSE)); + close_button->SizeToPreferredSize(); return close_button; } @@ -315,13 +314,14 @@ // there's no title. DCHECK(!title_margins_.IsEmpty() || !title_->visible()); - gfx::Rect bounds(GetContentsBounds()); + const gfx::Rect contents_bounds = GetContentsBounds(); + gfx::Rect bounds = contents_bounds; bounds.Inset(title_margins_); if (bounds.IsEmpty()) return; // The close button is positioned somewhat closer to the edge of the bubble. - gfx::Point close_position = GetContentsBounds().top_right(); + gfx::Point close_position = contents_bounds.top_right(); if (ui::MaterialDesignController::IsSecondaryUiMaterial()) { close_position += gfx::Vector2d(-close_->width() - kClosePaddingRightMd, kClosePaddingTopMd); @@ -353,11 +353,10 @@ bounds.set_height(title_height); if (footnote_container_) { - gfx::Rect local_bounds = GetContentsBounds(); - int height = footnote_container_->GetHeightForWidth(local_bounds.width()); - footnote_container_->SetBounds(local_bounds.x(), - local_bounds.bottom() - height, - local_bounds.width(), height); + const int width = contents_bounds.width(); + const int height = footnote_container_->GetHeightForWidth(width); + footnote_container_->SetBounds( + contents_bounds.x(), contents_bounds.bottom() - height, width, height); } }
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc index 54d6d1d..544da363 100644 --- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc +++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -390,6 +390,12 @@ } } +void DesktopNativeWidgetAura::SetNativeWindowProperty(const char* name, + void* value) { + if (content_window_) + content_window_->SetNativeWindowProperty(name, value); +} + //////////////////////////////////////////////////////////////////////////////// // DesktopNativeWidgetAura, internal::NativeWidgetPrivate implementation: @@ -595,12 +601,6 @@ drop_helper_->ResetTargetViewIfEquals(view); } -void DesktopNativeWidgetAura::SetNativeWindowProperty(const char* name, - void* value) { - if (content_window_) - content_window_->SetNativeWindowProperty(name, value); -} - void* DesktopNativeWidgetAura::GetNativeWindowProperty(const char* name) const { return content_window_ ? content_window_->GetNativeWindowProperty(name) : NULL;
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h index e45eb4d..3340fb2 100644 --- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h +++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
@@ -97,6 +97,9 @@ // we are being activated/deactivated. void HandleActivationChanged(bool active); + // Overridden from internal::NativeWidgetPrivate: + void SetNativeWindowProperty(const char* name, void* value) override; + protected: // Overridden from internal::NativeWidgetPrivate: void InitNativeWidget(const Widget::InitParams& params) override; @@ -114,7 +117,6 @@ const ui::Layer* GetLayer() const override; void ReorderNativeViews() override; void ViewRemoved(View* view) override; - void SetNativeWindowProperty(const char* name, void* value) override; void* GetNativeWindowProperty(const char* name) const override; TooltipManager* GetTooltipManager() const override; void SetCapture() override;
diff --git a/ui/webui/resources/.clang-format b/ui/webui/resources/.clang-format new file mode 100644 index 0000000..2baaa8d --- /dev/null +++ b/ui/webui/resources/.clang-format
@@ -0,0 +1,8 @@ +# Please keep this file the same as chrome/browser/resources/.clang-format. +BasedOnStyle: Chromium + +# Renaming quotes in <include> and <if> break things. +# For normal JS code, please prefer ' to ". +JavaScriptQuotes: Leave + +AllowShortFunctionsOnASingleLine: Empty
diff --git a/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html b/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html index 23bf68a6..6b2b6b60 100644 --- a/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html +++ b/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html
@@ -10,6 +10,8 @@ border: 0; border-radius: 2px; bottom: 0; + box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.12), + 0px 16px 16px rgba(0, 0, 0, 0.24); color: inherit; padding: 0; top: 0;
diff --git a/ui/webui/resources/cr_elements/cr_scrollable_behavior.js b/ui/webui/resources/cr_elements/cr_scrollable_behavior.js index 1858ccf..5589c18e 100644 --- a/ui/webui/resources/cr_elements/cr_scrollable_behavior.js +++ b/ui/webui/resources/cr_elements/cr_scrollable_behavior.js
@@ -113,8 +113,7 @@ 'can-scroll', scrollable.clientHeight < scrollable.scrollHeight); scrollable.classList.toggle('is-scrolled', scrollable.scrollTop > 0); scrollable.classList.toggle( - 'scrolled-to-bottom', - scrollable.scrollTop + scrollable.clientHeight >= + 'scrolled-to-bottom', scrollable.scrollTop + scrollable.clientHeight >= scrollable.scrollHeight); }, };
diff --git a/ui/webui/resources/css/text_defaults_md.css b/ui/webui/resources/css/text_defaults_md.css index b378ba293..56892a04 100644 --- a/ui/webui/resources/css/text_defaults_md.css +++ b/ui/webui/resources/css/text_defaults_md.css
@@ -23,7 +23,7 @@ body { font-family: Roboto, $i18nRaw{fontFamily}; - font-size: 81.25% + font-size: 81.25%; } button {
diff --git a/ui/webui/resources/js/analytics.js b/ui/webui/resources/js/analytics.js index a83fc807..6a467d37 100644 --- a/ui/webui/resources/js/analytics.js +++ b/ui/webui/resources/js/analytics.js
@@ -5,4 +5,4 @@ // This file serves as a proxy to bring the included js file from /third_party // into its correct location under the resources directory tree, whence it is // delivered via a chrome://resources URL. See ../webui_resources.grd. -<include src='../../../../third_party/analytics/google-analytics-bundle.js'> +<include src="../../../../third_party/analytics/google-analytics-bundle.js">
diff --git a/ui/webui/resources/js/cr/ui/bubble.js b/ui/webui/resources/js/cr/ui/bubble.js index 167ebf3d..fe97030 100644 --- a/ui/webui/resources/js/cr/ui/bubble.js +++ b/ui/webui/resources/js/cr/ui/bubble.js
@@ -191,9 +191,9 @@ left = Math.max(Math.min(left, max_left_pos), min_left_pos); var arrowTip = Math.min( Math.max( - arrow.width / 2, - this.arrowAtRight_ ? left + bubble.width - anchorMid : - anchorMid - left), + arrow.width / 2, this.arrowAtRight_ ? + left + bubble.width - anchorMid : + anchorMid - left), bubble.width - arrow.width / 2); // Work out the vertical placement, attempting to fit the bubble
diff --git a/ui/webui/resources/js/jstemplate_compiled.js b/ui/webui/resources/js/jstemplate_compiled.js index a394f3d..6a79748 100644 --- a/ui/webui/resources/js/jstemplate_compiled.js +++ b/ui/webui/resources/js/jstemplate_compiled.js
@@ -5,4 +5,4 @@ // This file serves as a proxy to bring the included js file from /third_party // into its correct location under the resources directory tree, whence it is // delivered via a chrome://resources URL. See ../webui_resources.grd. -<include src='../../../../third_party/jstemplate/jstemplate_compiled.js'> +<include src="../../../../third_party/jstemplate/jstemplate_compiled.js">