diff --git a/DEPS b/DEPS index dae3980a..62fb64d 100644 --- a/DEPS +++ b/DEPS
@@ -39,11 +39,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': 'ac66ae63eade5347864c5d0bd025756c5ea23fda', + 'skia_revision': 'e719577fe8ac3de38795cde2007337f854d97435', # 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': 'fd97bfe7190e2b515deed9c138c1617e675baa71', + 'v8_revision': '6596238f96a0254f040d5c8aefcdef0791ba2ba5', # 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. @@ -63,7 +63,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': '878dd5b121b38e6eaba5b3f3a90d0a9abf60feaf', + 'pdfium_revision': '7c29e27dae139a205755c1a29b7f3ac8b36ec0da', # 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. @@ -99,7 +99,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. - 'libfuzzer_revision': '3e02228ebfee620dd4a2db0ee15a8084ca349183', + 'libfuzzer_revision': '9aa0bddeb6820f6e5d897da410e1e8a3f7fd4b8e', } # Only these hosts are allowed for dependencies in this DEPS file.
diff --git a/WATCHLISTS b/WATCHLISTS index b87170c..bb2df83 100644 --- a/WATCHLISTS +++ b/WATCHLISTS
@@ -102,6 +102,9 @@ 'arc': { 'filepath': 'arc/', }, + 'arc_auth': { + 'filepath': 'chrome/browser/chromeos/arc/arc_auth' + }, 'arc_net': { 'filepath': 'components/arc/net/', }, @@ -1440,6 +1443,7 @@ 'hidehiko+watch@chromium.org', 'lhchavez+watch@chromium.org', 'yusukes+watch@chromium.org'], + 'arc_auth': ['khmel+watch@chromium.org'], 'arc_net': ['abhishekbh@chromium.org', 'cernekee@chromium.org', 'snanda@chromium.org'], @@ -1570,7 +1574,7 @@ 'blink_indexed_db': ['cmumford@chromium.org', 'jsbell+idb@chromium.org'], 'blink_input': ['dtapuska+blinkwatch@chromium.org', - 'nzolghadr+blinkwatch@chromium.org'], + 'nzolghadr@chromium.org'], 'blink_layers': ['blink-layers+watch@chromium.org'], 'blink_layout': ['blink-reviews-layout@chromium.org', 'eae+blinkwatch@chromium.org',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index a5b6062..0acd477 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -320,7 +320,6 @@ "common/system/keyboard_brightness_control_delegate.h", "common/system/locale/locale_notification_controller.cc", "common/system/locale/locale_notification_controller.h", - "common/system/locale/locale_observer.h", "common/system/networking_config_delegate.cc", "common/system/networking_config_delegate.h", "common/system/overview/overview_button_tray.cc",
diff --git a/ash/aura/wm_root_window_controller_aura.cc b/ash/aura/wm_root_window_controller_aura.cc index b6dafca..eae1b44 100644 --- a/ash/aura/wm_root_window_controller_aura.cc +++ b/ash/aura/wm_root_window_controller_aura.cc
@@ -114,12 +114,6 @@ WmRootWindowController::OnWallpaperAnimationFinished(widget); } -void WmRootWindowControllerAura::UpdateAfterLoginStatusChange( - LoginStatus status) { - root_window_controller_->UpdateAfterLoginStatusChange(status); - WmRootWindowController::UpdateAfterLoginStatusChange(status); -} - bool WmRootWindowControllerAura::ShouldDestroyWindowInCloseChildWindows( WmWindow* window) { return WmWindowAura::GetAuraWindow(window)->owned_by_parent();
diff --git a/ash/aura/wm_root_window_controller_aura.h b/ash/aura/wm_root_window_controller_aura.h index 976821b..0b888397 100644 --- a/ash/aura/wm_root_window_controller_aura.h +++ b/ash/aura/wm_root_window_controller_aura.h
@@ -42,7 +42,6 @@ gfx::Point GetLastMouseLocationInRoot() override; void OnInitialWallpaperAnimationStarted() override; void OnWallpaperAnimationFinished(views::Widget* widget) override; - void UpdateAfterLoginStatusChange(LoginStatus status) override; protected: // WmRootWindowController:
diff --git a/ash/common/mojo_interface_factory.cc b/ash/common/mojo_interface_factory.cc index e4384fe..b745571 100644 --- a/ash/common/mojo_interface_factory.cc +++ b/ash/common/mojo_interface_factory.cc
@@ -4,7 +4,10 @@ #include "ash/common/mojo_interface_factory.h" +#include <utility> + #include "ash/common/shelf/shelf_controller.h" +#include "ash/common/system/locale/locale_notification_controller.h" #include "ash/common/system/tray/system_tray_controller.h" #include "ash/common/wm_shell.h" #include "base/bind.h" @@ -14,6 +17,12 @@ namespace { +void BindLocaleNotificationControllerOnMainThread( + mojom::LocaleNotificationControllerRequest request) { + WmShell::Get()->locale_notification_controller()->BindRequest( + std::move(request)); +} + void BindShelfControllerRequestOnMainThread( mojom::ShelfControllerRequest request) { WmShell::Get()->shelf_controller()->BindRequest(std::move(request)); @@ -30,6 +39,9 @@ void RegisterInterfaces( service_manager::InterfaceRegistry* registry, scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner) { + registry->AddInterface( + base::Bind(&BindLocaleNotificationControllerOnMainThread), + main_thread_task_runner); registry->AddInterface(base::Bind(&BindShelfControllerRequestOnMainThread), main_thread_task_runner); registry->AddInterface(base::Bind(&BindSystemTrayRequestOnMainThread),
diff --git a/ash/common/system/locale/locale_notification_controller.cc b/ash/common/system/locale/locale_notification_controller.cc index 4a5e4a1..39444a7 100644 --- a/ash/common/system/locale/locale_notification_controller.cc +++ b/ash/common/system/locale/locale_notification_controller.cc
@@ -4,6 +4,7 @@ #include "ash/common/system/locale/locale_notification_controller.h" +#include <memory> #include <utility> #include "ash/common/system/system_notifier.h" @@ -28,7 +29,9 @@ class LocaleNotificationDelegate : public message_center::NotificationDelegate { public: - explicit LocaleNotificationDelegate(LocaleObserver::Delegate* delegate); + explicit LocaleNotificationDelegate( + const base::Callback<void(ash::mojom::LocaleNotificationResult)>& + callback); protected: ~LocaleNotificationDelegate() override; @@ -40,21 +43,28 @@ void ButtonClick(int button_index) override; private: - LocaleObserver::Delegate* delegate_; + base::Callback<void(ash::mojom::LocaleNotificationResult)> callback_; DISALLOW_COPY_AND_ASSIGN(LocaleNotificationDelegate); }; LocaleNotificationDelegate::LocaleNotificationDelegate( - LocaleObserver::Delegate* delegate) - : delegate_(delegate) { - DCHECK(delegate_); + const base::Callback<void(ash::mojom::LocaleNotificationResult)>& callback) + : callback_(callback) {} + +LocaleNotificationDelegate::~LocaleNotificationDelegate() { + if (callback_) { + // We're being destroyed but the user didn't click on anything. Run the + // callback so that we don't crash. + callback_.Run(ash::mojom::LocaleNotificationResult::ACCEPT); + } } -LocaleNotificationDelegate::~LocaleNotificationDelegate() {} - void LocaleNotificationDelegate::Close(bool by_user) { - delegate_->AcceptLocaleChange(); + if (callback_) { + callback_.Run(ash::mojom::LocaleNotificationResult::ACCEPT); + callback_.Reset(); + } } bool LocaleNotificationDelegate::HasClickedListener() { @@ -62,32 +72,37 @@ } void LocaleNotificationDelegate::Click() { - delegate_->AcceptLocaleChange(); + if (callback_) { + callback_.Run(ash::mojom::LocaleNotificationResult::ACCEPT); + callback_.Reset(); + } } void LocaleNotificationDelegate::ButtonClick(int button_index) { DCHECK_EQ(0, button_index); - delegate_->RevertLocaleChange(); + + if (callback_) { + callback_.Run(ash::mojom::LocaleNotificationResult::REVERT); + callback_.Reset(); + } } } // namespace -LocaleNotificationController::LocaleNotificationController() { - WmShell::Get()->system_tray_notifier()->AddLocaleObserver(this); -} +LocaleNotificationController::LocaleNotificationController() {} -LocaleNotificationController::~LocaleNotificationController() { - WmShell::Get()->system_tray_notifier()->RemoveLocaleObserver(this); +LocaleNotificationController::~LocaleNotificationController() {} + +void LocaleNotificationController::BindRequest( + mojom::LocaleNotificationControllerRequest request) { + bindings_.AddBinding(this, std::move(request)); } void LocaleNotificationController::OnLocaleChanged( - LocaleObserver::Delegate* delegate, const std::string& cur_locale, const std::string& from_locale, - const std::string& to_locale) { - if (!delegate) - return; - + const std::string& to_locale, + const OnLocaleChangedCallback& callback) { base::string16 from = l10n_util::GetDisplayNameForLocale(from_locale, cur_locale, true); base::string16 to = @@ -109,7 +124,7 @@ base::string16() /* display_source */, GURL(), message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT, system_notifier::kNotifierLocale), - optional, new LocaleNotificationDelegate(delegate))); + optional, new LocaleNotificationDelegate(callback))); message_center::MessageCenter::Get()->AddNotification( std::move(notification)); }
diff --git a/ash/common/system/locale/locale_notification_controller.h b/ash/common/system/locale/locale_notification_controller.h index 24f27a6f..b97c7ed 100644 --- a/ash/common/system/locale/locale_notification_controller.h +++ b/ash/common/system/locale/locale_notification_controller.h
@@ -7,28 +7,37 @@ #include <string> -#include "ash/common/system/locale/locale_observer.h" +#include "ash/public/interfaces/locale.mojom.h" #include "base/macros.h" +#include "mojo/public/cpp/bindings/binding_set.h" namespace ash { // Observes the locale change and creates rich notification for the change. -class LocaleNotificationController : public LocaleObserver { +class LocaleNotificationController + : public mojom::LocaleNotificationController { public: LocaleNotificationController(); ~LocaleNotificationController() override; + // Binds the mojom::LocaleNotificationController interface request to this + // object. + void BindRequest(mojom::LocaleNotificationControllerRequest request); + private: - // Overridden from LocaleObserver. - void OnLocaleChanged(LocaleObserver::Delegate* delegate, - const std::string& cur_locale, + // Overridden from mojom::LocaleNotificationController: + void OnLocaleChanged(const std::string& cur_locale, const std::string& from_locale, - const std::string& to_locale) override; + const std::string& to_locale, + const OnLocaleChangedCallback& callback) override; std::string cur_locale_; std::string from_locale_; std::string to_locale_; + // Bindings for the LocaleNotificationController interface. + mojo::BindingSet<mojom::LocaleNotificationController> bindings_; + DISALLOW_COPY_AND_ASSIGN(LocaleNotificationController); };
diff --git a/ash/common/system/locale/locale_observer.h b/ash/common/system/locale/locale_observer.h deleted file mode 100644 index 96b76d4..0000000 --- a/ash/common/system/locale/locale_observer.h +++ /dev/null
@@ -1,34 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef ASH_COMMON_SYSTEM_LOCALE_LOCALE_OBSERVER_H_ -#define ASH_COMMON_SYSTEM_LOCALE_LOCALE_OBSERVER_H_ - -#include <string> - -#include "ash/ash_export.h" - -namespace ash { - -class ASH_EXPORT LocaleObserver { - public: - class Delegate { - public: - virtual ~Delegate() {} - - virtual void AcceptLocaleChange() = 0; - virtual void RevertLocaleChange() = 0; - }; - - virtual ~LocaleObserver() {} - - virtual void OnLocaleChanged(Delegate* delegate, - const std::string& cur_locale, - const std::string& from_locale, - const std::string& to_locale) = 0; -}; - -} // namespace ash - -#endif // ASH_COMMON_SYSTEM_LOCALE_LOCALE_OBSERVER_H_
diff --git a/ash/common/system/tray/system_tray_notifier.cc b/ash/common/system/tray/system_tray_notifier.cc index b424aa0..62b79b48b 100644 --- a/ash/common/system/tray/system_tray_notifier.cc +++ b/ash/common/system/tray/system_tray_notifier.cc
@@ -129,22 +129,6 @@ observer.OnIMEMenuActivationChanged(is_active); } -void SystemTrayNotifier::AddLocaleObserver(LocaleObserver* observer) { - locale_observers_.AddObserver(observer); -} - -void SystemTrayNotifier::RemoveLocaleObserver(LocaleObserver* observer) { - locale_observers_.RemoveObserver(observer); -} - -void SystemTrayNotifier::NotifyLocaleChanged(LocaleObserver::Delegate* delegate, - const std::string& cur_locale, - const std::string& from_locale, - const std::string& to_locale) { - for (auto& observer : locale_observers_) - observer.OnLocaleChanged(delegate, cur_locale, from_locale, to_locale); -} - void SystemTrayNotifier::AddUpdateObserver(UpdateObserver* observer) { update_observers_.AddObserver(observer); }
diff --git a/ash/common/system/tray/system_tray_notifier.h b/ash/common/system/tray/system_tray_notifier.h index 29bb9f5..3051575 100644 --- a/ash/common/system/tray/system_tray_notifier.h +++ b/ash/common/system/tray/system_tray_notifier.h
@@ -9,7 +9,6 @@ #include "ash/ash_export.h" #include "ash/common/accessibility_types.h" -#include "ash/common/system/locale/locale_observer.h" #include "base/macros.h" #include "base/observer_list.h" @@ -78,14 +77,6 @@ void NotifyRefreshIME(); void NotifyRefreshIMEMenu(bool is_active); - // Locale. - void AddLocaleObserver(LocaleObserver* observer); - void RemoveLocaleObserver(LocaleObserver* observer); - void NotifyLocaleChanged(LocaleObserver::Delegate* delegate, - const std::string& cur_locale, - const std::string& from_locale, - const std::string& to_locale); - // OS updates. void AddUpdateObserver(UpdateObserver* observer); void RemoveUpdateObserver(UpdateObserver* observer); @@ -173,7 +164,6 @@ base::ObserverList<AudioObserver> audio_observers_; base::ObserverList<ClockObserver> clock_observers_; base::ObserverList<IMEObserver> ime_observers_; - base::ObserverList<LocaleObserver> locale_observers_; base::ObserverList<UpdateObserver> update_observers_; base::ObserverList<UserObserver> user_observers_;
diff --git a/ash/common/wm/root_window_layout_manager.cc b/ash/common/wm/root_window_layout_manager.cc index b3e9f52..dda5384 100644 --- a/ash/common/wm/root_window_layout_manager.cc +++ b/ash/common/wm/root_window_layout_manager.cc
@@ -29,10 +29,18 @@ WmWindowTracker children_tracker(owner_->GetChildren()); while (!children_tracker.windows().empty()) { WmWindow* child = children_tracker.Pop(); + // Skip descendants of top-level windows, i.e. only resize containers and + // other windows without a delegate, such as ScreenDimmer windows. + if (child->GetToplevelWindow()) + continue; + child->SetBounds(fullscreen_bounds); WmWindowTracker grandchildren_tracker(child->GetChildren()); - while (!grandchildren_tracker.windows().empty()) - grandchildren_tracker.Pop()->SetBounds(fullscreen_bounds); + while (!grandchildren_tracker.windows().empty()) { + child = grandchildren_tracker.Pop(); + if (!child->GetToplevelWindow()) + child->SetBounds(fullscreen_bounds); + } } }
diff --git a/ash/common/wm_shell.cc b/ash/common/wm_shell.cc index b78a7939..7bee335a 100644 --- a/ash/common/wm_shell.cc +++ b/ash/common/wm_shell.cc
@@ -23,6 +23,7 @@ #include "ash/common/shell_window_ids.h" #include "ash/common/system/brightness_control_delegate.h" #include "ash/common/system/keyboard_brightness_control_delegate.h" +#include "ash/common/system/locale/locale_notification_controller.h" #include "ash/common/system/toast/toast_manager.h" #include "ash/common/system/tray/system_tray_controller.h" #include "ash/common/system/tray/system_tray_delegate.h" @@ -233,6 +234,8 @@ : delegate_(std::move(shell_delegate)), focus_cycler_(base::MakeUnique<FocusCycler>()), immersive_context_(base::MakeUnique<ImmersiveContextAsh>()), + locale_notification_controller_( + base::MakeUnique<LocaleNotificationController>()), shelf_controller_(base::MakeUnique<ShelfController>()), system_tray_controller_(base::MakeUnique<SystemTrayController>( delegate_->GetShellConnector())),
diff --git a/ash/common/wm_shell.h b/ash/common/wm_shell.h index f1e45dd..ab46e16 100644 --- a/ash/common/wm_shell.h +++ b/ash/common/wm_shell.h
@@ -54,6 +54,7 @@ class KeyEventWatcher; class KeyboardBrightnessControlDelegate; class KeyboardUI; +class LocaleNotificationController; class MaximizeModeController; class MruWindowTracker; class NewWindowDelegate; @@ -128,6 +129,10 @@ KeyboardUI* keyboard_ui() { return keyboard_ui_.get(); } + LocaleNotificationController* locale_notification_controller() { + return locale_notification_controller_.get(); + } + MaximizeModeController* maximize_mode_controller() { return maximize_mode_controller_.get(); } @@ -472,6 +477,7 @@ std::unique_ptr<KeyboardBrightnessControlDelegate> keyboard_brightness_control_delegate_; std::unique_ptr<KeyboardUI> keyboard_ui_; + std::unique_ptr<LocaleNotificationController> locale_notification_controller_; std::unique_ptr<MaximizeModeController> maximize_mode_controller_; std::unique_ptr<MediaDelegate> media_delegate_; std::unique_ptr<MruWindowTracker> mru_window_tracker_;
diff --git a/ash/mus/manifest.json b/ash/mus/manifest.json index 76ffade..1fab12a1 100644 --- a/ash/mus/manifest.json +++ b/ash/mus/manifest.json
@@ -5,6 +5,7 @@ "capabilities": { "provided": { "ash": [ + "ash::mojom::LocaleNotificationController", "ash::mojom::ShelfController", "ash::mojom::SystemTray", "ash::mojom::WallpaperController"
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn index 0570864b..c2e08a11 100644 --- a/ash/public/interfaces/BUILD.gn +++ b/ash/public/interfaces/BUILD.gn
@@ -8,6 +8,7 @@ sources = [ "ash_window_type.mojom", "container.mojom", + "locale.mojom", "shelf.mojom", "system_tray.mojom", "wallpaper.mojom",
diff --git a/ash/public/interfaces/locale.mojom b/ash/public/interfaces/locale.mojom new file mode 100644 index 0000000..756430d8 --- /dev/null +++ b/ash/public/interfaces/locale.mojom
@@ -0,0 +1,21 @@ +// 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. + +module ash.mojom; + +// Sent as the response to LocaleNotificationController.OnLocaleChanged(). +enum LocaleNotificationResult { + ACCEPT, + REVERT +}; + +// Used by Chrome to make ash show a notification. +interface LocaleNotificationController { + // Displays a notification in ash prompting the user whether to accept a + // change in the locale. If the user clicks the accept button (or closes the + // notification), OnLocaleChange() returns ACCEPT. If the user clicks the + // revert button, returns REVERT. + OnLocaleChanged(string current, string from, string to) + => (LocaleNotificationResult result); +};
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc index 286f072..12cfeda 100644 --- a/ash/root_window_controller.cc +++ b/ash/root_window_controller.cc
@@ -63,11 +63,9 @@ #include "ui/aura/client/aura_constants.h" #include "ui/aura/client/screen_position_client.h" #include "ui/aura/window.h" -#include "ui/aura/window_delegate.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/aura/window_observer.h" #include "ui/aura/window_tracker.h" -#include "ui/base/hit_test.h" #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/keyboard/keyboard_controller.h" @@ -145,43 +143,6 @@ return true; } -// A window delegate which does nothing. Used to create a window that -// is a event target, but do nothing. -class EmptyWindowDelegate : public aura::WindowDelegate { - public: - EmptyWindowDelegate() {} - ~EmptyWindowDelegate() override {} - - // aura::WindowDelegate overrides: - gfx::Size GetMinimumSize() const override { return gfx::Size(); } - gfx::Size GetMaximumSize() const override { return gfx::Size(); } - void OnBoundsChanged(const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds) override {} - gfx::NativeCursor GetCursor(const gfx::Point& point) override { - return gfx::kNullCursor; - } - int GetNonClientComponent(const gfx::Point& point) const override { - return HTNOWHERE; - } - bool ShouldDescendIntoChildForEventHandling( - aura::Window* child, - const gfx::Point& location) override { - return false; - } - bool CanFocus() override { return false; } - void OnCaptureLost() override {} - void OnPaint(const ui::PaintContext& context) override {} - void OnDeviceScaleFactorChanged(float device_scale_factor) override {} - void OnWindowDestroying(aura::Window* window) override {} - void OnWindowDestroyed(aura::Window* window) override { delete this; } - void OnWindowTargetVisibilityChanged(bool visible) override {} - bool HasHitTestMask() const override { return false; } - void GetHitTestMask(gfx::Path* mask) const override {} - - private: - DISALLOW_COPY_AND_ASSIGN(EmptyWindowDelegate); -}; - } // namespace void RootWindowController::CreateForPrimaryDisplay(AshWindowTreeHost* host) { @@ -311,11 +272,6 @@ return ash_host_->AsWindowTreeHost()->window()->GetChildById(container_id); } -void RootWindowController::UpdateAfterLoginStatusChange(LoginStatus status) { - if (status != LoginStatus::NOT_LOGGED_IN) - mouse_event_target_.reset(); -} - void RootWindowController::OnInitialWallpaperAnimationStarted() { #if defined(OS_CHROMEOS) if (base::CommandLine::ForCurrentProcess()->HasSwitch( @@ -338,8 +294,6 @@ } void RootWindowController::CloseChildWindows() { - mouse_event_target_.reset(); - // Remove observer as deactivating keyboard causes // docked_window_layout_manager() to fire notifications. if (docked_window_layout_manager() && @@ -530,20 +484,6 @@ status_container->SetEventTargeter(base::MakeUnique<ShelfWindowTargeter>( wm_status_container, wm_shelf_aura_.get())); - if (!WmShell::Get() - ->GetSessionStateDelegate() - ->IsActiveUserSessionStarted()) { - // This window exists only to be a event target on login screen. - // It does not have to handle events, nor be visible. - mouse_event_target_.reset(new aura::Window(new EmptyWindowDelegate)); - mouse_event_target_->Init(ui::LAYER_NOT_DRAWN); - - aura::Window* lock_wallpaper_container = - GetContainer(kShellWindowId_LockScreenWallpaperContainer); - lock_wallpaper_container->AddChild(mouse_event_target_.get()); - mouse_event_target_->Show(); - } - panel_container_handler_ = base::MakeUnique<PanelWindowEventHandler>(); GetContainer(kShellWindowId_PanelContainer) ->AddPreTargetHandler(panel_container_handler_.get());
diff --git a/ash/root_window_controller.h b/ash/root_window_controller.h index 2cd9a7c6..f70d2dd 100644 --- a/ash/root_window_controller.h +++ b/ash/root_window_controller.h
@@ -143,10 +143,6 @@ aura::Window* GetContainer(int container_id); const aura::Window* GetContainer(int container_id) const; - // Called when the login status changes after login (such as lock/unlock). - // TODO(oshima): Investigate if we can merge this and |OnLoginStateChanged|. - void UpdateAfterLoginStatusChange(LoginStatus status); - // Called when the brightness/grayscale animation from white to the login // wallpaper image has started. Starts |boot_splash_screen_|'s hiding // animation (if the screen is non-NULL). @@ -233,13 +229,6 @@ // to it during construction of the shelf widget and status tray. std::unique_ptr<WmShelfAura> wm_shelf_aura_; - // An invisible/empty window used as a event target for - // |MouseCursorEventFilter| before a user logs in. - // (crbug.com/266987) - // Its container is |LockScreenWallpaperContainer| and - // this must be deleted before the container is deleted. - std::unique_ptr<aura::Window> mouse_event_target_; - std::unique_ptr<SystemWallpaperController> system_wallpaper_; #if defined(OS_CHROMEOS)
diff --git a/ash/root_window_controller_unittest.cc b/ash/root_window_controller_unittest.cc index 969a5f1..192ec46 100644 --- a/ash/root_window_controller_unittest.cc +++ b/ash/root_window_controller_unittest.cc
@@ -695,20 +695,6 @@ delete window2; } -typedef test::NoSessionAshTestBase NoSessionRootWindowControllerTest; - -// Make sure that an event handler exists for entire display area. -TEST_F(NoSessionRootWindowControllerTest, Event) { - aura::Window* root = Shell::GetPrimaryRootWindow(); - const gfx::Size size = root->bounds().size(); - EXPECT_TRUE(root->GetEventHandlerForPoint(gfx::Point(0, 0))); - EXPECT_TRUE(root->GetEventHandlerForPoint(gfx::Point(0, size.height() - 1))); - EXPECT_TRUE(root->GetEventHandlerForPoint(gfx::Point(size.width() - 1, 0))); - EXPECT_TRUE(root->GetEventHandlerForPoint(gfx::Point(0, size.height() - 1))); - EXPECT_TRUE(root->GetEventHandlerForPoint( - gfx::Point(size.width() - 1, size.height() - 1))); -} - class VirtualKeyboardRootWindowControllerTest : public RootWindowControllerTest { public:
diff --git a/ash/shell.cc b/ash/shell.cc index a5d27a9..896dfd7b 100644 --- a/ash/shell.cc +++ b/ash/shell.cc
@@ -26,7 +26,6 @@ #include "ash/common/shelf/wm_shelf.h" #include "ash/common/shell_delegate.h" #include "ash/common/shell_window_ids.h" -#include "ash/common/system/locale/locale_notification_controller.h" #include "ash/common/system/status_area_widget.h" #include "ash/common/system/tray/system_tray_delegate.h" #include "ash/common/wallpaper/wallpaper_delegate.h" @@ -475,8 +474,6 @@ root->GetRootWindowController()->GetShelf()->ShutdownShelfWidget(); wm_shell_->DeleteSystemTrayDelegate(); - locale_notification_controller_.reset(); - // Drag-and-drop must be canceled prior to close all windows. drag_drop_controller_.reset(); @@ -778,8 +775,6 @@ wm_shell_->SetSystemTrayDelegate( base::WrapUnique(wm_shell_->delegate()->CreateSystemTrayDelegate())); - locale_notification_controller_.reset(new LocaleNotificationController); - #if defined(OS_CHROMEOS) // Create TouchTransformerController before // WindowTreeHostManager::InitDisplays()
diff --git a/ash/shell.h b/ash/shell.h index 7989a78..a5f5e6e 100644 --- a/ash/shell.h +++ b/ash/shell.h
@@ -87,7 +87,6 @@ class ImmersiveHandlerFactoryAsh; class LaserPointerController; class LinkHandlerModelFactory; -class LocaleNotificationController; class LockStateController; enum class LoginStatus; class MagnificationController; @@ -494,7 +493,6 @@ std::unique_ptr<DisplayConfigurationController> display_configuration_controller_; - std::unique_ptr<LocaleNotificationController> locale_notification_controller_; std::unique_ptr<ScreenPinningController> screen_pinning_controller_; #if defined(OS_CHROMEOS)
diff --git a/base/task_scheduler/scheduler_worker.cc b/base/task_scheduler/scheduler_worker.cc index 59b756c..e64ca61 100644 --- a/base/task_scheduler/scheduler_worker.cc +++ b/base/task_scheduler/scheduler_worker.cc
@@ -217,8 +217,6 @@ } void SchedulerWorker::JoinForTesting() { - // It's not possible to have concurrent calls to this method because a DCHECK - // fails when AtomicFlag::Set() calls aren't sequenced. DCHECK(!should_exit_for_testing_.IsSet()); should_exit_for_testing_.Set();
diff --git a/blimp/BUILD.gn b/blimp/BUILD.gn index cdc5752..03ca1ae 100644 --- a/blimp/BUILD.gn +++ b/blimp/BUILD.gn
@@ -52,6 +52,7 @@ deps = [ "//blimp/client:unit_tests", "//blimp/common:unit_tests", + "//blimp/helium:unit_tests", "//blimp/net:unit_tests", "//blimp/test:run_all_unittests", ]
diff --git a/blimp/helium/result.h b/blimp/helium/result.h index 5d17e01a..bbc2f1e 100644 --- a/blimp/helium/result.h +++ b/blimp/helium/result.h
@@ -5,6 +5,8 @@ #ifndef BLIMP_HELIUM_RESULT_H_ #define BLIMP_HELIUM_RESULT_H_ +#include "blimp/helium/blimp_helium_export.h" + namespace blimp { namespace helium { @@ -22,7 +24,7 @@ }; // Gets a human-readable string representation of |result|. -const char* ResultToString(Result result); +BLIMP_HELIUM_EXPORT const char* ResultToString(Result result); } // namespace helium } // namespace blimp
diff --git a/blimp/helium/result_unittest.cc b/blimp/helium/result_unittest.cc index bc68740..3f11fc76 100644 --- a/blimp/helium/result_unittest.cc +++ b/blimp/helium/result_unittest.cc
@@ -17,7 +17,7 @@ ~ResultTest() override {} }; -TEST_F(ResultTest, ResultToString) { +TEST_F(ResultTest, ResultToStringWorks) { // The exhaustive list of errors need not be specified here, but enough are // specified that we can verify that the switch/case mapping works as // intended.
diff --git a/blimp/helium/stream.h b/blimp/helium/stream.h index 119e433..2df1aeb 100644 --- a/blimp/helium/stream.h +++ b/blimp/helium/stream.h
@@ -48,4 +48,4 @@ } // namespace helium } // namespace blimp -#endif // BLIMP_NET_HELIUM_HELIUM_STREAM_H_ +#endif // BLIMP_HELIUM_STREAM_H_
diff --git a/build/args/bots/chromium.fyi/blimp_client_rel.gn b/build/args/bots/chromium.fyi/blimp_client_rel.gn index a25f072..b59d55e 100644 --- a/build/args/bots/chromium.fyi/blimp_client_rel.gn +++ b/build/args/bots/chromium.fyi/blimp_client_rel.gn
@@ -1,3 +1,4 @@ import("//build/args/blimp_client.gn") is_debug = false dcheck_always_on = true +use_goma = true
diff --git a/build/args/bots/chromium.fyi/blimp_engine_rel.gn b/build/args/bots/chromium.fyi/blimp_engine_rel.gn index 7a8261a..05dc14b61 100644 --- a/build/args/bots/chromium.fyi/blimp_engine_rel.gn +++ b/build/args/bots/chromium.fyi/blimp_engine_rel.gn
@@ -9,3 +9,4 @@ is_debug = false is_official_build = true dcheck_always_on = true +use_goma = true
diff --git a/cc/test/layer_tree_host_remote_for_testing.cc b/cc/test/layer_tree_host_remote_for_testing.cc index edba23d..9c48b89 100644 --- a/cc/test/layer_tree_host_remote_for_testing.cc +++ b/cc/test/layer_tree_host_remote_for_testing.cc
@@ -68,9 +68,7 @@ } void BeginMainFrameNotExpectedSoon() override {} void DidBeginMainFrame() override {} - void UpdateLayerTreeHost() override { - layer_tree_host_remote_->UpdateStateOnInProcessHost(); - } + void UpdateLayerTreeHost() override {} void ApplyViewportDeltas(const gfx::Vector2dF& inner_delta, const gfx::Vector2dF& outer_delta, const gfx::Vector2dF& elastic_overscroll_delta, @@ -290,26 +288,35 @@ void LayerTreeHostRemoteForTesting::ProcessRemoteCompositorUpdate( std::unique_ptr<CompositorProtoState> compositor_proto_state) { - pending_compositor_proto_state_ = std::move(compositor_proto_state); -} + DCHECK(layer_tree_host_in_process_->CommitRequested()); + // Deserialize the update from the remote host into client side LTH in + // process. This bypasses the network layer. + const proto::LayerTreeHost& layer_tree_host_proto = + compositor_proto_state->compositor_message->layer_tree_host(); + compositor_state_deserializer_->DeserializeCompositorUpdate( + layer_tree_host_proto); -void LayerTreeHostRemoteForTesting::UpdateStateOnInProcessHost() { - // When the InProcess host asks us to update, we de-serialize the update from - // the remote host. - if (pending_compositor_proto_state_) { - const proto::LayerTreeHost& layer_tree_host_proto = - pending_compositor_proto_state_->compositor_message->layer_tree_host(); - compositor_state_deserializer_->DeserializeCompositorUpdate( - layer_tree_host_proto); + const proto::LayerUpdate& layer_updates = + compositor_proto_state->compositor_message->layer_tree_host() + .layer_updates(); + for (int i = 0; i < layer_updates.layers_size(); ++i) { + int engine_layer_id = layer_updates.layers(i).id(); + Layer* engine_layer = GetLayerTree()->LayerById(engine_layer_id); + Layer* client_layer = + compositor_state_deserializer_->GetLayerForEngineId(engine_layer_id); - pending_compositor_proto_state_ = nullptr; - - // The only case where the remote host would give a compositor update is if - // they wanted the main frame to go till the commit pipeline stage. So - // request one to make sure that the in process main frame also goes till - // the commit step. - layer_tree_host_in_process_->SetNeedsCommit(); + // Copy test only layer data that are not serialized into network messages. + // So in test cases, layers on the client have the same states as their + // corresponding layers on the engine. + client_layer->SetForceRenderSurfaceForTesting( + engine_layer->force_render_surface_for_testing()); } + + // The only case where the remote host would give a compositor update is if + // they wanted the main frame to go till the commit pipeline stage. So + // request one to make sure that the in process main frame also goes till + // the commit step. + layer_tree_host_in_process_->SetNeedsCommit(); } } // namespace cc
diff --git a/cc/test/layer_tree_host_remote_for_testing.h b/cc/test/layer_tree_host_remote_for_testing.h index 14389b9..2af6722 100644 --- a/cc/test/layer_tree_host_remote_for_testing.h +++ b/cc/test/layer_tree_host_remote_for_testing.h
@@ -94,12 +94,10 @@ void RemoteHostNeedsMainFrame(); void ProcessRemoteCompositorUpdate( std::unique_ptr<CompositorProtoState> compositor_proto_state); - void UpdateStateOnInProcessHost(); std::unique_ptr<LayerTreeHostInProcess> layer_tree_host_in_process_; std::unique_ptr<CompositorStateDeserializer> compositor_state_deserializer_; - std::unique_ptr<CompositorProtoState> pending_compositor_proto_state_; ScrollOffsetMap layers_scrolled_; std::unique_ptr<LayerTreeHostInProcessClient>
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc index 0cf3b4c0..e0eb2f0 100644 --- a/cc/trees/layer_tree_host_unittest.cc +++ b/cc/trees/layer_tree_host_unittest.cc
@@ -81,25 +81,25 @@ class LayerTreeHostTestHasImplThreadTest : public LayerTreeHostTest { public: - LayerTreeHostTestHasImplThreadTest() : threaded_(false) {} + LayerTreeHostTestHasImplThreadTest() : single_threaded_(false) {} void RunTest(CompositorMode mode) override { - threaded_ = mode == CompositorMode::THREADED; + single_threaded_ = mode == CompositorMode::SINGLE_THREADED; LayerTreeHostTest::RunTest(mode); } void BeginTest() override { - EXPECT_EQ(threaded_, HasImplThread()); + EXPECT_EQ(single_threaded_, !HasImplThread()); EndTest(); } - void AfterTest() override { EXPECT_EQ(threaded_, HasImplThread()); } + void AfterTest() override { EXPECT_EQ(single_threaded_, !HasImplThread()); } private: - bool threaded_; + bool single_threaded_; }; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestHasImplThreadTest); +SINGLE_MULTI_AND_REMOTE_TEST_F(LayerTreeHostTestHasImplThreadTest); class LayerTreeHostTestSetNeedsCommitInsideLayout : public LayerTreeHostTest { protected: @@ -118,7 +118,7 @@ void AfterTest() override {} }; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsCommitInsideLayout); +SINGLE_MULTI_AND_REMOTE_TEST_F(LayerTreeHostTestSetNeedsCommitInsideLayout); class LayerTreeHostTestFrameOrdering : public LayerTreeHostTest { protected: @@ -193,7 +193,7 @@ ImplOrder impl_ = IMPL_START; }; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestFrameOrdering); +SINGLE_MULTI_AND_REMOTE_TEST_F(LayerTreeHostTestFrameOrdering); class LayerTreeHostTestSetNeedsUpdateInsideLayout : public LayerTreeHostTest { protected: @@ -212,7 +212,7 @@ void AfterTest() override {} }; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsUpdateInsideLayout); +SINGLE_MULTI_AND_REMOTE_TEST_F(LayerTreeHostTestSetNeedsUpdateInsideLayout); // Test if the LTHI receives ReadyToActivate notifications from the TileManager // when no raster tasks get scheduled. @@ -256,7 +256,7 @@ size_t required_for_activation_count_; }; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestReadyToActivateEmpty); +SINGLE_MULTI_AND_REMOTE_TEST_F(LayerTreeHostTestReadyToActivateEmpty); // Test if the LTHI receives ReadyToActivate notifications from the TileManager // when some raster tasks flagged as REQUIRED_FOR_ACTIVATION got scheduled. @@ -285,9 +285,9 @@ FakeContentLayerClient client_; }; -// Multi-thread only because in single thread the commit goes directly to the -// active tree, so notify ready to activate is skipped. -MULTI_THREAD_TEST_F(LayerTreeHostTestReadyToActivateNonEmpty); +// No single thread test because the commit goes directly to the active tree in +// single thread mode, so notify ready to activate is skipped. +REMOTE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestReadyToActivateNonEmpty); // Test if the LTHI receives ReadyToDraw notifications from the TileManager when // no raster tasks get scheduled. @@ -327,7 +327,7 @@ size_t required_for_draw_count_; }; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestReadyToDrawEmpty); +SINGLE_MULTI_AND_REMOTE_TEST_F(LayerTreeHostTestReadyToDrawEmpty); // Test if the LTHI receives ReadyToDraw notifications from the TileManager when // some raster tasks flagged as REQUIRED_FOR_DRAW got scheduled. @@ -434,6 +434,8 @@ // single threaded mode. SINGLE_THREAD_TEST_F(LayerTreeHostTestReadyToDrawVisibility); +// Since the LayerTreeHostContextCacheTests exclusively tests the behavior of +// LayerTreeHostImpl, they don't need to run for the remote mode. class LayerTreeHostContextCacheTest : public LayerTreeHostTest { public: std::unique_ptr<TestCompositorFrameSink> CreateCompositorFrameSink( @@ -654,7 +656,7 @@ int num_draws_; }; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsCommit1); +SINGLE_MULTI_AND_REMOTE_TEST_F(LayerTreeHostTestSetNeedsCommit1); // A SetNeedsCommit should lead to 1 commit. Issuing a second commit after that // first committed frame draws should lead to another commit. @@ -690,7 +692,7 @@ int num_draws_; }; -MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsCommit2); +REMOTE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsCommit2); // Verify that we pass property values in PushPropertiesTo. class LayerTreeHostTestPushPropertiesTo : public LayerTreeHostTest { @@ -785,7 +787,7 @@ int index_; }; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestPushPropertiesTo); +SINGLE_MULTI_AND_REMOTE_TEST_F(LayerTreeHostTestPushPropertiesTo); class LayerTreeHostTestPushNodeOwnerToNodeIdMap : public LayerTreeHostTest { protected: @@ -907,6 +909,11 @@ scoped_refptr<Layer> child_; }; +// This test compares the value of tree indexes from Layers on the engine to the +// resulting PropertyTrees copied to the pending tree after the commit on the +// client. This will result in a failure for LTH remote test since while the +// client side Layers would have the correct values for these indexes, but the +// engine will never build PropertyTrees in LTH remote. See crbug.com/655795. SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestPushNodeOwnerToNodeIdMap); class LayerTreeHostTestSurfaceDamage : public LayerTreeHostTest { @@ -988,6 +995,12 @@ scoped_refptr<Layer> grand_child_; }; +// This test changes properties on the Layer and ensures that the subtree damage +// is tracked correctly on the resulting RenderSurfaceImpl for the corresponding +// LayerImpl. Since the remote code path currently synchronizes the hierarchy +// between the engine and client for every frame, all Layers on the client end +// up being marked as damaged. +// Enable this when crbug.com/605170 is fixed. SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSurfaceDamage); // Verify damage status of property trees is preserved after commit. @@ -1199,6 +1212,9 @@ FilterOperations sepia_filter_; }; +// This test verifies that correct values are retained on the impl thread in +// cases where they are animated on that thread. Since remote mode doesn't +// support threaded animations, we don't need to run this in remote mode. SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestEffectTreeSync); class LayerTreeHostTestTransformTreeSync : public LayerTreeHostTest { @@ -1276,6 +1292,9 @@ scoped_refptr<Layer> root_; }; +// This test verifies that correct values are retained on the impl thread in +// cases where they are animated on that thread. Since remote mode doesn't +// support threaded animations, we don't need to run this in remote mode. SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestTransformTreeSync); // Verify damage status is updated even when the transform tree doesn't need @@ -1350,6 +1369,9 @@ scoped_refptr<Layer> grand_child_; }; +// This test verifies that correct values are retained on the impl thread in +// cases where they are animated on that thread. Since remote mode doesn't +// support threaded animations, we don't need to run this in remote mode. SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestTransformTreeDamageIsUpdated); // Test that when mask layers switches layers, this gets pushed onto impl. @@ -1420,6 +1442,12 @@ int index_; }; +// This test also verifies that the Layers updated in a main frame correspond +// only to the Layers that need updates computed using +// draw_property_utils::FindLayersThatNeedUpdates. Since LayerTreeHostRemote +// currently updates all Layers during the main frame, the test fails for remote +// mode. +// TODO(xingliu): Revisit enabling this when crbug.com/650885 is resolved. SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSwitchMaskLayer); // 1 setNeedsRedraw after the first commit has completed should lead to 1 @@ -1456,7 +1484,7 @@ int num_draws_; }; -MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsRedraw); +REMOTE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsRedraw); // After setNeedsRedrawRect(invalid_rect) the final damage_rect // must contain invalid_rect. @@ -1514,7 +1542,7 @@ scoped_refptr<FakePictureLayer> root_layer_; }; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsRedrawRect); +SINGLE_MULTI_AND_REMOTE_TEST_F(LayerTreeHostTestSetNeedsRedrawRect); // Ensure the texture size of the pending and active trees are identical when a // layer is not in the viewport and a resize happens on the viewport
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorView.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorView.java index fe2200e..7531207 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorView.java
@@ -21,6 +21,7 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.widget.Button; import android.widget.CheckBox; @@ -428,6 +429,21 @@ } }); } + + // If some of the required fields are valid, then this EditorView is for modification, so + // validate form to update displayed errors and hide keyboard by default. + if (hasValidRequiredField()) { + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + mHandler.post(new Runnable() { + @Override + public void run() { + validateForm(); + } + }); + } else { + getWindow().setSoftInputMode( + WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + } } /** Rereads the values in the model to update the UI. */ @@ -456,6 +472,15 @@ } } + private boolean hasValidRequiredField() { + List<EditorFieldModel> fields = mEditorModel.getFields(); + for (int i = 0; i < fields.size(); i++) { + EditorFieldModel field = fields.get(i); + if (field.isRequired() && field.isValid()) return true; + } + return false; + } + private List<EditorFieldView> getViewsWithInvalidInformation(boolean findAll) { List<EditorFieldView> invalidViews = new ArrayList<>(); for (int i = 0; i < mFieldViews.size(); i++) {
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index bff3c0d..0c176b9 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp
@@ -2296,9 +2296,6 @@ <message name="IDS_OPTIONS_SETTINGS_SECTION_TITLE_BLUETOOTH"> Bluetooth </message> - <message name="IDS_OPTIONS_SETTINGS_SECTION_TITLE_LANGUAGE"> - Language - </message> <message name="IDS_OPTIONS_DEVICE_GROUP_BATTERY_STATUS_LABEL" desc="The prefix to the battery status in the device section."> Battery - <ph name="status">$1<ex>56% - 2:03 left</ex></ph> </message> @@ -5696,14 +5693,6 @@ Visit captive portal login page </message> - <!-- Locale Change Notification--> - <message name="IDS_LOCALE_CHANGE_MESSAGE" desc="Message shown when locale was changed based on profile content."> - Chrome's language has changed from "<ph name="FROM_LOCALE">$1<ex>Italian</ex></ph>" to "<ph name="TO_LOCALE">$2<ex>English (United States)</ex></ph>" after syncing your settings. - </message> - <message name="IDS_LOCALE_CHANGE_REVERT_MESSAGE" desc="Link to revert a change."> - Change back to "<ph name="FROM_LOCALE">$1<ex>Italian</ex></ph>" (requires sign-out) - </message> - <!-- 3G data Notifications --> <message name="IDS_3G_NOTIFICATION_MESSAGE" desc="Text of the 3G data notification telling that 3G data is enabled."> Google Chrome will use cellular data if you're not connected to another network.
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index e6ddb0ba..216c326 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -6929,6 +6929,13 @@ Enables an experiment to switch menu labels that use 'Save as...' to 'Download'. </message> + <message name="IDS_FLAGS_ENABLE_ENUMERATING_AUDIO_DEVICES_NAME" desc="Name of the flag to experimentally enable enumerating audio devices on ChromeOS."> + Experimentally enable enumerating audio devices. + </message> + <message name="IDS_FLAGS_ENABLE_ENUMERATING_AUDIO_DEVICES_DESCRIPTION" desc="Description of the flag that experimentally enables enumerating audio devices on ChromeOS."> + Experimentally enable the use of enumerating audio devices. + </message> + <!-- WebRTC logs --> <message name="IDS_WEBRTC_LOGS_TITLE" desc="Title for the chrome://webrtc-logs page."> WebRTC logs @@ -14811,10 +14818,10 @@ <!-- Strings for ARC intent picker --> <if expr="chromeos"> <message name="IDS_INTENT_PICKER_BUBBLE_VIEW_ALWAYS" desc="Message contained in a button shown to the user in order to choose and remember an app selection."> - ALWAYS + Always </message> <message name="IDS_INTENT_PICKER_BUBBLE_VIEW_JUST_ONCE" desc="Message contained in a button shown to the user in order to choose to open a URL in a given app only once."> - JUST ONCE + Just once </message> <message name="IDS_INTENT_PICKER_BUBBLE_VIEW_OPEN_WITH" desc="Message contained in a label used as a header for a list of applications from which the user will pick one."> Open with
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp index 794e974..b78e7e0 100644 --- a/chrome/app/settings_strings.grdp +++ b/chrome/app/settings_strings.grdp
@@ -1509,7 +1509,10 @@ <message name="IDS_SETTINGS_MANAGE_CERTIFICATES_DESCRIPTION" desc="Secondary, continued explanation of how to manage SSL certificates and settings in Privacy options"> Manage HTTPS/SSL certificates and settings </message> - <message name="IDS_SETTINGS_SITE_SETTINGS" desc="Text of the button that takes a user to settings page thats allows users to modify site settings. Also the title of that settings page."> + <message name="IDS_SETTINGS_CONTENT_SETTINGS" desc="Text of the button that takes a user to settings page thats allows users to modify site settings. Also the title of that settings page."> + Content Settings + </message> + <message name="IDS_SETTINGS_SITE_SETTINGS" desc="Text of the button that takes a user to the enhanced settings page thats allows users to modify site settings. Also the title of that settings page."> Site Settings </message> <message name="IDS_SETTINGS_SITE_SETTINGS_DESCRIPTION" desc="Secondary, continued explanation of what content settings in Chrome do">
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index e9bf8c06..4fefb1d4 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -69,6 +69,7 @@ #include "content/public/common/feature_h264_with_openh264_ffmpeg.h" #include "content/public/common/features.h" #include "gin/public/gin_features.h" +#include "media/audio/audio_features.h" #include "media/base/media_switches.h" #include "media/midi/midi_switches.h" #include "ui/base/ui_base_switches.h" @@ -611,7 +612,7 @@ {IDS_FLAGS_WEBRTC_HW_ENCODING_NONE, switches::kDisableWebRtcHWEncoding, switches::kDisableWebRtcHWEncodingNone}, }; -#endif +#endif // ENABLE_WEBRTC // RECORDING USER METRICS FOR FLAGS: // ----------------------------------------------------------------------------- @@ -753,7 +754,7 @@ kOsLinux | kOsCrOS | kOsWin, ENABLE_DISABLE_VALUE_TYPE(switches::kEnableOverlayScrollbar, switches::kDisableOverlayScrollbar)}, -#endif // USE_AURA) || defined(OS_LINUX +#endif // USE_AURA || OS_LINUX { // See http://crbug.com/120416 for how to remove this flag. "save-page-as-mhtml", IDS_FLAGS_SAVE_PAGE_AS_MHTML_NAME, IDS_FLAGS_SAVE_PAGE_AS_MHTML_DESCRIPTION, kOsMac | kOsWin | kOsLinux, @@ -822,7 +823,7 @@ {"enable-android-spellchecker", IDS_OPTIONS_ENABLE_SPELLCHECK, IDS_OPTIONS_ENABLE_ANDROID_SPELLCHECKER_DESCRIPTION, kOsAndroid, FEATURE_VALUE_TYPE(spellcheck::kAndroidSpellChecker)}, -#endif // ENABLE_SPELLCHECK) && defined(OS_ANDROID +#endif // ENABLE_SPELLCHECK && OS_ANDROID {"enable-scroll-prediction", IDS_FLAGS_SCROLL_PREDICTION_NAME, IDS_FLAGS_SCROLL_PREDICTION_DESCRIPTION, kOsDesktop, SINGLE_VALUE_TYPE(switches::kEnableScrollPrediction)}, @@ -1219,7 +1220,7 @@ IDS_FLAGS_RESET_APP_LIST_INSTALL_STATE_DESCRIPTION, kOsMac | kOsWin | kOsLinux, SINGLE_VALUE_TYPE(app_list::switches::kResetAppListInstallState)}, -#endif // ENABLE_APP_LIST +#endif // BUILDFLAG(ENABLE_APP_LIST) #if defined(OS_ANDROID) {"enable-downloads-ui", IDS_FLAGS_ENABLE_DOWNLOADS_UI_NAME, IDS_FLAGS_ENABLE_DOWNLOADS_UI_DESCRIPTION, kOsAndroid, @@ -1278,14 +1279,14 @@ {"translate-2016q2-ui", IDS_FLAGS_TRANSLATE_2016Q2_UI_NAME, IDS_FLAGS_TRANSLATE_2016Q2_UI_DESCRIPTION, kOsCrOS | kOsWin | kOsLinux, FEATURE_VALUE_TYPE(translate::kTranslateUI2016Q2)}, -#endif // OS_LINUX) || defined(OS_WIN) || defined(OS_CHROMEOS +#endif // OS_LINUX || OS_WIN || OS_CHROMEOS {"translate-lang-by-ulp", IDS_FLAGS_TRANSLATE_LANGUAGE_BY_ULP_NAME, IDS_FLAGS_TRANSLATE_LANGUAGE_BY_ULP_DESCRIPTION, kOsAll, FEATURE_VALUE_TYPE(translate::kTranslateLanguageByULP)}, #if defined(OS_MACOSX) {"enable-native-notifications", IDS_NOTIFICATIONS_NATIVE_FLAG, IDS_NOTIFICATIONS_NATIVE_FLAG_DESCRIPTION, kOsMac, - SINGLE_VALUE_TYPE(switches::kEnableNativeNotifications)}, + FEATURE_VALUE_TYPE(features::kNativeNotifications)}, #endif // OS_MACOSX #if defined(TOOLKIT_VIEWS) {"disable-views-rect-based-targeting", @@ -1409,7 +1410,7 @@ kOsDesktop, SINGLE_VALUE_TYPE( switches::kEnableMessageCenterAlwaysScrollUpUponNotificationRemoval)}, -#endif // OS_ANDROID +#endif // !OS_ANDROID {"enable-md-policy-page", IDS_FLAGS_ENABLE_MATERIAL_DESIGN_POLICY_PAGE_NAME, IDS_FLAGS_ENABLE_MATERIAL_DESIGN_POLICY_PAGE_DESCRIPTION, kOsDesktop, SINGLE_VALUE_TYPE(switches::kEnableMaterialDesignPolicyPage)}, @@ -1535,7 +1536,7 @@ "1", switches::kMediaRouter, "0")}, -#endif // ENABLE_MEDIA_ROUTER) && !defined(OS_ANDROID +#endif // ENABLE_MEDIA_ROUTER && !OS_ANDROID // Since Drive Search is not available when app list is disabled, flag guard // enable-drive-search-in-chrome-launcher flag. #if BUILDFLAG(ENABLE_APP_LIST) @@ -1545,7 +1546,7 @@ ENABLE_DISABLE_VALUE_TYPE( app_list::switches::kEnableDriveSearchInChromeLauncher, app_list::switches::kDisableDriveSearchInChromeLauncher)}, -#endif // ENABLE_APP_LIST +#endif // BUILDFLAG(ENABLE_APP_LIST) #if defined(OS_CHROMEOS) {"disable-mtp-write-support", IDS_FLAGS_MTP_WRITE_SUPPORT_NAME, IDS_FLAGS_MTP_WRITE_SUPPORT_DESCRIPTION, kOsCrOS, @@ -1695,7 +1696,7 @@ IDS_FLAGS_ENABLE_WEB_NOTIFICATION_CUSTOM_LAYOUTS_DESCRIPTION, kOsAndroid, ENABLE_DISABLE_VALUE_TYPE(switches::kEnableWebNotificationCustomLayouts, switches::kDisableWebNotificationCustomLayouts)}, -#endif // ENABLE_NOTIFICATIONS) && defined(OS_ANDROID +#endif // ENABLE_NOTIFICATIONS && OS_ANDROID #if defined(OS_WIN) {"enable-appcontainer", IDS_FLAGS_ENABLE_APPCONTAINER_NAME, IDS_FLAGS_ENABLE_APPCONTAINER_DESCRIPTION, kOsWin, @@ -1709,7 +1710,7 @@ ENABLE_DISABLE_VALUE_TYPE( autofill::switches::kEnableOfferUploadCreditCards, autofill::switches::kDisableOfferUploadCreditCards)}, -#endif // TOOLKIT_VIEWS) || defined(OS_ANDROID +#endif // TOOLKIT_VIEWS || OS_ANDROID #if defined(OS_ANDROID) {"tab-management-experiment-type", IDS_FLAGS_HERB_PROTOTYPE_CHOICES_NAME, IDS_FLAGS_HERB_PROTOTYPE_CHOICES_DESCRIPTION, kOsAndroid, @@ -1744,7 +1745,7 @@ IDS_FLAGS_ENABLE_INPUT_IME_API_DESCRIPTION, kOsWin | kOsLinux, ENABLE_DISABLE_VALUE_TYPE(switches::kEnableInputImeAPI, switches::kDisableInputImeAPI)}, -#endif // OS_WIN) || defined(OS_LINUX +#endif // OS_WIN || OS_LINUX {"enable-origin-trials", IDS_FLAGS_ORIGIN_TRIALS_NAME, IDS_FLAGS_ORIGIN_TRIALS_DESCRIPTION, kOsAll, FEATURE_VALUE_TYPE(features::kOriginTrials)}, @@ -1856,8 +1857,7 @@ IDS_FLAGS_WEBRTC_H264_WITH_OPENH264_FFMPEG_NAME, IDS_FLAGS_WEBRTC_H264_WITH_OPENH264_FFMPEG_DESCRIPTION, kOsDesktop, FEATURE_VALUE_TYPE(content::kWebRtcH264WithOpenH264FFmpeg)}, -#endif // ENABLE_WEBRTC) && BUILDFLAG(RTC_USE_H264 && - // !defined(MEDIA_DISABLE_FFMPEG) +#endif // ENABLE_WEBRTC && BUILDFLAG(RTC_USE_H264) && !MEDIA_DISABLE_FFMPEG #if defined(OS_ANDROID) {"ime-thread", IDS_FLAGS_IME_THREAD_NAME, IDS_FLAGS_IME_THREAD_DESCRIPTION, kOsAndroid, FEATURE_VALUE_TYPE(features::kImeThread)}, @@ -1905,13 +1905,13 @@ {"enable-files-details-panel", IDS_FLAGS_ENABLE_FILES_DETAILS_PANEL_NAME, IDS_FLAGS_ENABLE_FILES_DETAILS_PANEL_DESCRIPTION, kOsCrOS, SINGLE_VALUE_TYPE(chromeos::switches::kEnableFilesDetailsPanel)}, -#endif // defined(OS_CHROMEOS) +#endif // OS_CHROMEOS #if !defined(OS_ANDROID) && !defined(OS_IOS) && defined(GOOGLE_CHROME_BUILD) {"enable-google-branded-context-menu", IDS_FLAGS_GOOGLE_BRANDED_CONTEXT_MENU_NAME, IDS_FLAGS_GOOGLE_BRANDED_CONTEXT_MENU_DESCRIPTION, kOsDesktop, SINGLE_VALUE_TYPE(switches::kEnableGoogleBrandedContextMenu)}, -#endif // OS_ANDROID) && !defined(OS_IOS) && defined(GOOGLE_CHROME_BUILD +#endif // !OS_ANDROID && !OS_IOS && GOOGLE_CHROME_BUILD #if defined(OS_MACOSX) {"enable-fullscreen-in-tab-detaching", IDS_FLAGS_TAB_DETACHING_IN_FULLSCREEN_NAME, @@ -2021,12 +2021,10 @@ chromeos::switches::kArcUseAuthEndpoint, "https://www-googleapis-staging.sandbox.google.com/oauth2/v4/" "ExchangeToken")}, -#endif // OS_CHROMEOS -#if defined(OS_CHROMEOS) {"arc-boot-completed-broadcast", IDS_FLAGS_ARC_BOOT_COMPLETED, IDS_FLAGS_ARC_BOOT_COMPLETED_DESCRIPTION, kOsCrOS, FEATURE_VALUE_TYPE(arc::kBootCompletedBroadcastFeature)}, -#endif +#endif // OS_CHROMEOS #if defined(OS_WIN) {"disable-winrt-midi-api", IDS_FLAGS_DISABLE_WINRT_MIDI_API_NAME, IDS_FLAGS_DISABLE_WINRT_MIDI_API_DESCRIPTION, kOsWin, @@ -2042,12 +2040,18 @@ IDS_FLAGS_EXPENSIVE_BACKGROUND_TIMER_THROTTLING_NAME, IDS_FLAGS_EXPENSIVE_BACKGROUND_TIMER_THROTTLING_DESCRIPTION, kOsAll, FEATURE_VALUE_TYPE(features::kExpensiveBackgroundTimerThrottling)}, - {"security-chip", IDS_FLAGS_SECURITY_CHIP_NAME, + {"security-chip", IDS_FLAGS_SECURITY_CHIP_NAME, IDS_FLAGS_SECURITY_CHIP_DESCRIPTION, kOsDesktop, MULTI_VALUE_TYPE(kSecurityChipChoices)}, {"security-chip-animation", IDS_FLAGS_SECURITY_CHIP_ANIMATION_NAME, IDS_FLAGS_SECURITY_CHIP_ANIMATION_DESCRIPTION, kOsDesktop, MULTI_VALUE_TYPE(kSecurityChipAnimationChoices)}, +#if defined(OS_CHROMEOS) + {"enumerate-audio-devices", IDS_FLAGS_ENABLE_ENUMERATING_AUDIO_DEVICES_NAME, + IDS_FLAGS_ENABLE_ENUMERATING_AUDIO_DEVICES_DESCRIPTION, kOsCrOS, + FEATURE_VALUE_TYPE(features::kEnumerateAudioDevices)}, +#endif // 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/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm index 01449b0..aca30de6 100644 --- a/chrome/browser/app_controller_mac.mm +++ b/chrome/browser/app_controller_mac.mm
@@ -1574,8 +1574,8 @@ } - (void)delayedScreenParametersUpdate { - FOR_EACH_OBSERVER(ui::WorkAreaWatcherObserver, workAreaChangeObservers_, - WorkAreaChanged()); + for (auto& observer : workAreaChangeObservers_) + observer.WorkAreaChanged(); } - (BOOL)application:(NSApplication*)application
diff --git a/chrome/browser/background/background_application_list_model.cc b/chrome/browser/background/background_application_list_model.cc index c37dae63..dfe9e251 100644 --- a/chrome/browser/background/background_application_list_model.cc +++ b/chrome/browser/background/background_application_list_model.cc
@@ -330,8 +330,8 @@ void BackgroundApplicationListModel::SendApplicationDataChangedNotifications( const Extension* extension) { - FOR_EACH_OBSERVER(Observer, observers_, OnApplicationDataChanged(extension, - profile_)); + for (auto& observer : observers_) + observer.OnApplicationDataChanged(extension, profile_); } void BackgroundApplicationListModel::OnExtensionLoaded( @@ -398,6 +398,7 @@ } if (old_cursor != extensions_.end() || new_cursor != extensions.end()) { extensions_ = extensions; - FOR_EACH_OBSERVER(Observer, observers_, OnApplicationListChanged(profile_)); + for (auto& observer : observers_) + observer.OnApplicationListChanged(profile_); } }
diff --git a/chrome/browser/background/background_contents.cc b/chrome/browser/background/background_contents.cc index 7192ee7a..3d3d824d 100644 --- a/chrome/browser/background/background_contents.cc +++ b/chrome/browser/background/background_contents.cc
@@ -99,9 +99,8 @@ chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED, content::Source<Profile>(profile_), content::Details<BackgroundContents>(this)); - FOR_EACH_OBSERVER(extensions::DeferredStartRenderHostObserver, - deferred_start_render_host_observer_list_, - OnDeferredStartRenderHostDestroyed(this)); + for (auto& observer : deferred_start_render_host_observer_list_) + observer.OnDeferredStartRenderHostDestroyed(this); extension_host_delegate_->GetExtensionHostQueue()->Remove(this); } @@ -174,17 +173,15 @@ void BackgroundContents::DidStartLoading() { // BackgroundContents only loads once, so this can only be the first time it // has started loading. - FOR_EACH_OBSERVER(extensions::DeferredStartRenderHostObserver, - deferred_start_render_host_observer_list_, - OnDeferredStartRenderHostDidStartFirstLoad(this)); + for (auto& observer : deferred_start_render_host_observer_list_) + observer.OnDeferredStartRenderHostDidStartFirstLoad(this); } void BackgroundContents::DidStopLoading() { // BackgroundContents only loads once, so this can only be the first time // it has stopped loading. - FOR_EACH_OBSERVER(extensions::DeferredStartRenderHostObserver, - deferred_start_render_host_observer_list_, - OnDeferredStartRenderHostDidStopFirstLoad(this)); + for (auto& observer : deferred_start_render_host_observer_list_) + observer.OnDeferredStartRenderHostDidStopFirstLoad(this); } void BackgroundContents::Observe(int type,
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 449912a..87dd713 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -3129,8 +3129,6 @@ throttles.push_back(MergeSessionNavigationThrottle::Create(handle)); } - // TODO(djacobo): Support incognito mode by showing an aditional dialog as a - // warning that the selected app is not in incognito mode. const arc::ArcAuthService* auth_service = arc::ArcAuthService::Get(); if (auth_service && auth_service->IsArcEnabled() && !handle->GetWebContents()->GetBrowserContext()->IsOffTheRecord()) {
diff --git a/chrome/browser/chrome_content_browser_manifest_overlay.json b/chrome/browser/chrome_content_browser_manifest_overlay.json index 23481711..fc4c671 100644 --- a/chrome/browser/chrome_content_browser_manifest_overlay.json +++ b/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -16,6 +16,7 @@ "metrics::mojom::CallStackProfileCollector" ], "ash": [ + "ash::mojom::LocaleNotificationController", "ash::mojom::ShelfController", "ash::mojom::SystemTray", "ash::mojom::SystemTrayClient"
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index 7cb3aa43..7ec4616 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -834,8 +834,6 @@ "login/ui/keyboard_driven_oobe_key_handler.h", "login/ui/lock_window.cc", "login/ui/lock_window.h", - "login/ui/lock_window_aura.cc", - "login/ui/lock_window_aura.h", "login/ui/login_display.cc", "login/ui/login_display.h", "login/ui/login_display_host.cc",
diff --git a/chrome/browser/chromeos/arc/arc_external_protocol_dialog.cc b/chrome/browser/chromeos/arc/arc_external_protocol_dialog.cc index e4bf0cc..35157fd4 100644 --- a/chrome/browser/chromeos/arc/arc_external_protocol_dialog.cc +++ b/chrome/browser/chromeos/arc/arc_external_protocol_dialog.cc
@@ -70,15 +70,32 @@ int routing_id, const GURL& url, mojo::Array<mojom::IntentHandlerInfoPtr> handlers, - size_t selected_app_index, + std::string selected_app_package, ArcNavigationThrottle::CloseReason close_reason) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + size_t selected_app_index = handlers.size(); // Make sure that the instance at least supports HandleUrl. auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( "HandleUrl", kMinVersionForHandleUrl); - if (!instance || selected_app_index >= handlers.size()) + if (!instance) { close_reason = ArcNavigationThrottle::CloseReason::ERROR; + } else if (close_reason == + ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED || + close_reason == + ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED) { + // If the user selected an app to continue the navigation, confirm that the + // |package_name| matches a valid option and return the index. + for (size_t i = 0; i < handlers.size(); ++i) { + if (handlers[i]->package_name == selected_app_package) { + selected_app_index = i; + break; + } + } + + if (selected_app_index == handlers.size()) + close_reason = ArcNavigationThrottle::CloseReason::ERROR; + } switch (close_reason) { case ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED: { @@ -127,15 +144,16 @@ std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> icons) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - using NameAndIcon = std::pair<std::string, gfx::Image>; - std::vector<NameAndIcon> app_info; + using AppInfo = arc::ArcNavigationThrottle::AppInfo; + std::vector<AppInfo> app_info; for (const auto& handler : handlers) { const ActivityIconLoader::ActivityName activity(handler->package_name, handler->activity_name); const auto it = icons->find(activity); app_info.emplace_back( - handler->name, it != icons->end() ? it->second.icon20 : gfx::Image()); + AppInfo(it != icons->end() ? it->second.icon20 : gfx::Image(), + handler->package_name, handler->name)); } auto show_bubble_cb = base::Bind(ShowIntentPickerBubble());
diff --git a/chrome/browser/chromeos/arc/arc_navigation_throttle.cc b/chrome/browser/chromeos/arc/arc_navigation_throttle.cc index d2d290b2..87f1acb4 100644 --- a/chrome/browser/chromeos/arc/arc_navigation_throttle.cc +++ b/chrome/browser/chromeos/arc/arc_navigation_throttle.cc
@@ -173,7 +173,8 @@ << "Chrome browser is selected as the preferred app for this URL: " << navigation_handle()->GetURL().spec(); } - OnIntentPickerClosed(std::move(handlers), i, + std::string package_name = handlers[i]->package_name; + OnIntentPickerClosed(std::move(handlers), package_name, CloseReason::PREFERRED_ACTIVITY_FOUND); return; } @@ -212,7 +213,7 @@ mojo::Array<mojom::IntentHandlerInfoPtr> handlers, std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> icons) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - std::vector<NameAndIcon> app_info; + std::vector<AppInfo> app_info; for (const auto& handler : handlers) { gfx::Image icon; @@ -221,7 +222,8 @@ const auto it = icons->find(activity); app_info.emplace_back( - handler->name, it != icons->end() ? it->second.icon20 : gfx::Image()); + AppInfo(it != icons->end() ? it->second.icon20 : gfx::Image(), + handler->package_name, handler->name)); } show_intent_picker_callback_.Run( @@ -232,19 +234,34 @@ void ArcNavigationThrottle::OnIntentPickerClosed( mojo::Array<mojom::IntentHandlerInfoPtr> handlers, - size_t selected_app_index, + std::string selected_app_package, CloseReason close_reason) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); const GURL& url = navigation_handle()->GetURL(); content::NavigationHandle* handle = navigation_handle(); - previous_user_action_ = close_reason; // Make sure that the instance at least supports HandleUrl. auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( "HandleUrl", kMinVersionForHandleUrl); - if (!instance || selected_app_index >= handlers.size()) + size_t selected_app_index = handlers.size(); + if (!instance) { close_reason = CloseReason::ERROR; + } else if (close_reason == CloseReason::JUST_ONCE_PRESSED || + close_reason == CloseReason::ALWAYS_PRESSED || + close_reason == CloseReason::PREFERRED_ACTIVITY_FOUND) { + // Since we are selecting an app by its package name, we need to locate it + // on the |handlers| structure before sending the IPC to ARC. + for (size_t i = 0; i < handlers.size(); ++i) { + if (handlers[i]->package_name == selected_app_package) { + selected_app_index = i; + break; + } + } + + if (selected_app_index == handlers.size()) + close_reason = CloseReason::ERROR; + } switch (close_reason) { case CloseReason::ERROR: @@ -272,8 +289,7 @@ handlers[selected_app_index]->package_name)) { handle->Resume(); } else { - instance->HandleUrl(url.spec(), - handlers[selected_app_index]->package_name); + instance->HandleUrl(url.spec(), selected_app_package); handle->CancelDeferredNavigation( content::NavigationThrottle::CANCEL_AND_IGNORE); if (handle->GetWebContents()->GetController().IsInitialNavigation())
diff --git a/chrome/browser/chromeos/arc/arc_navigation_throttle.h b/chrome/browser/chromeos/arc/arc_navigation_throttle.h index 188cadf..18be29a 100644 --- a/chrome/browser/chromeos/arc/arc_navigation_throttle.h +++ b/chrome/browser/chromeos/arc/arc_navigation_throttle.h
@@ -47,11 +47,18 @@ // ScrollView. enum { kMaxAppResults = 3 }; - using NameAndIcon = std::pair<std::string, gfx::Image>; - using ShowIntentPickerCallback = - base::Callback<void(content::WebContents* web_contents, - const std::vector<NameAndIcon>& app_info, - const base::Callback<void(size_t, CloseReason)>& cb)>; + struct AppInfo { + explicit AppInfo(gfx::Image img, std::string package, std::string activity) + : icon(img), package_name(package), activity_name(activity) {} + gfx::Image icon; + std::string package_name; + std::string activity_name; + }; + + using ShowIntentPickerCallback = base::Callback<void( + content::WebContents* web_contents, + const std::vector<AppInfo>& app_info, + const base::Callback<void(std::string, CloseReason)>& cb)>; ArcNavigationThrottle(content::NavigationHandle* navigation_handle, const ShowIntentPickerCallback& show_intent_picker_cb); ~ArcNavigationThrottle() override; @@ -71,7 +78,7 @@ mojo::Array<mojom::IntentHandlerInfoPtr> handlers, std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> icons); void OnIntentPickerClosed(mojo::Array<mojom::IntentHandlerInfoPtr> handlers, - size_t selected_app_index, + std::string selected_app_package, CloseReason close_reason); // A callback object that allow us to display an IntentPicker when Run() is // executed, it also allow us to report the user's selection back to
diff --git a/chrome/browser/chromeos/arc/arc_support_host.cc b/chrome/browser/chromeos/arc/arc_support_host.cc index 7867122..214c0554 100644 --- a/chrome/browser/chromeos/arc/arc_support_host.cc +++ b/chrome/browser/chromeos/arc/arc_support_host.cc
@@ -233,7 +233,7 @@ "learnMoreLocationServices", l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_LEARN_MORE_LOCATION_SERVICES)); loadtime_data->SetString( - "learnMoreClose", + "overlayClose", l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_LEARN_MORE_CLOSE)); const std::string& app_locale = g_browser_process->GetApplicationLocale();
diff --git a/chrome/browser/chromeos/locale_change_guard.cc b/chrome/browser/chromeos/locale_change_guard.cc index 1f26ce5..13e1af9e 100644 --- a/chrome/browser/chromeos/locale_change_guard.cc +++ b/chrome/browser/chromeos/locale_change_guard.cc
@@ -6,8 +6,6 @@ #include <algorithm> -#include "ash/common/system/tray/system_tray_notifier.h" -#include "ash/common/wm_shell.h" #include "base/bind.h" #include "base/macros.h" #include "base/strings/utf_string_conversions.h" @@ -17,15 +15,17 @@ #include "chrome/browser/chromeos/settings/device_settings_service.h" #include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/ash/ash_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/common/pref_names.h" -#include "chrome/grit/generated_resources.h" #include "components/prefs/pref_service.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.h" #include "content/public/browser/user_metrics.h" #include "content/public/browser/web_contents.h" +#include "content/public/common/service_manager_connection.h" +#include "services/service_manager/public/cpp/connector.h" #include "ui/base/l10n/l10n_util.h" using base::UserMetricsAction; @@ -63,6 +63,23 @@ content::NotificationService::AllBrowserContextsAndSources()); } +void LocaleChangeGuard::ConnectToLocaleNotificationController() { + content::ServiceManagerConnection* connection = + content::ServiceManagerConnection::GetForProcess(); + service_manager::Connector* connector = + connection ? connection->GetConnector() : nullptr; + // Unit tests may not have a connector. + if (!connector) + return; + + if (chrome::IsRunningInMash()) { + connector->ConnectToInterface("service:ash", ¬ification_controller_); + } else { + connector->ConnectToInterface("service:content_browser", + ¬ification_controller_); + } +} + void LocaleChangeGuard::RevertLocaleChange() { if (profile_ == NULL || from_locale_.empty() || @@ -176,8 +193,23 @@ PrepareChangingLocale(from_locale, to_locale); } - ash::WmShell::Get()->system_tray_notifier()->NotifyLocaleChanged( - this, cur_locale, from_locale_, to_locale_); + if (!notification_controller_) + ConnectToLocaleNotificationController(); + + notification_controller_->OnLocaleChanged( + cur_locale, from_locale_, to_locale_, + base::Bind(&LocaleChangeGuard::OnResult, AsWeakPtr())); +} + +void LocaleChangeGuard::OnResult(ash::mojom::LocaleNotificationResult result) { + switch (result) { + case ash::mojom::LocaleNotificationResult::ACCEPT: + AcceptLocaleChange(); + break; + case ash::mojom::LocaleNotificationResult::REVERT: + RevertLocaleChange(); + break; + } } void LocaleChangeGuard::AcceptLocaleChange() { @@ -211,20 +243,6 @@ from_locale_ = from_locale; if (!to_locale.empty()) to_locale_ = to_locale; - - if (!from_locale_.empty() && !to_locale_.empty()) { - base::string16 from = l10n_util::GetDisplayNameForLocale( - from_locale_, cur_locale, true); - base::string16 to = l10n_util::GetDisplayNameForLocale( - to_locale_, cur_locale, true); - - title_text_ = l10n_util::GetStringUTF16( - IDS_OPTIONS_SETTINGS_SECTION_TITLE_LANGUAGE); - message_text_ = l10n_util::GetStringFUTF16( - IDS_LOCALE_CHANGE_MESSAGE, from, to); - revert_link_text_ = l10n_util::GetStringFUTF16( - IDS_LOCALE_CHANGE_REVERT_MESSAGE, from); - } } // static
diff --git a/chrome/browser/chromeos/locale_change_guard.h b/chrome/browser/chromeos/locale_change_guard.h index 3d53f92..8a6c16f 100644 --- a/chrome/browser/chromeos/locale_change_guard.h +++ b/chrome/browser/chromeos/locale_change_guard.h
@@ -10,7 +10,7 @@ #include <memory> #include <string> -#include "ash/common/system/locale/locale_observer.h" +#include "ash/public/interfaces/locale.mojom.h" #include "base/compiler_specific.h" #include "base/gtest_prod_util.h" #include "base/lazy_instance.h" @@ -32,16 +32,11 @@ // (based on synchronized user preference). If so: shows notification that // allows user to revert change. class LocaleChangeGuard : public content::NotificationObserver, - public ash::LocaleObserver::Delegate, public base::SupportsWeakPtr<LocaleChangeGuard> { public: explicit LocaleChangeGuard(Profile* profile); ~LocaleChangeGuard() override; - // ash::LocaleChangeDelegate implementation. - void AcceptLocaleChange() override; - void RevertLocaleChange() override; - // Called just before changing locale. void PrepareChangingLocale( const std::string& from_locale, const std::string& to_locale); @@ -55,9 +50,15 @@ FRIEND_TEST_ALL_PREFIXES(LocaleChangeGuardTest, ShowNotificationLocaleChangedList); + void ConnectToLocaleNotificationController(); + void RevertLocaleChangeCallback(const base::ListValue* list); void Check(); + void OnResult(ash::mojom::LocaleNotificationResult result); + void AcceptLocaleChange(); + void RevertLocaleChange(); + // content::NotificationObserver implementation. void Observe(int type, const content::NotificationSource& source, @@ -70,6 +71,9 @@ static const char* const* GetSkipShowNotificationLanguagesForTesting(); static size_t GetSkipShowNotificationLanguagesSizeForTesting(); + // Ash's mojom::LocaleNotificationController used to display notifications. + ash::mojom::LocaleNotificationControllerPtr notification_controller_; + std::string from_locale_; std::string to_locale_; Profile* profile_; @@ -78,12 +82,7 @@ bool main_frame_loaded_; content::NotificationRegistrar registrar_; - // We want to show locale change notification in previous language however - // we cannot directly load strings for non-current locale. So we cache - // messages before locale change. - base::string16 title_text_; - base::string16 message_text_; - base::string16 revert_link_text_; + DISALLOW_COPY_AND_ASSIGN(LocaleChangeGuard); }; } // namespace chromeos
diff --git a/chrome/browser/chromeos/login/lock/webui_screen_locker.cc b/chrome/browser/chromeos/login/lock/webui_screen_locker.cc index 630fb5d..5b64189 100644 --- a/chrome/browser/chromeos/login/lock/webui_screen_locker.cc +++ b/chrome/browser/chromeos/login/lock/webui_screen_locker.cc
@@ -80,7 +80,7 @@ gfx::Rect bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); lock_time_ = base::TimeTicks::Now(); - LockWindow* lock_window = LockWindow::Create(); + auto* lock_window = new LockWindow(); lock_window->set_observer(this); lock_window->set_initially_focused_view(this); lock_window_ = lock_window->GetWidget();
diff --git a/chrome/browser/chromeos/login/ui/lock_window.cc b/chrome/browser/chromeos/login/ui/lock_window.cc index 5a609c8..c9a4392 100644 --- a/chrome/browser/chromeos/login/ui/lock_window.cc +++ b/chrome/browser/chromeos/login/ui/lock_window.cc
@@ -4,11 +4,47 @@ #include "chrome/browser/chromeos/login/ui/lock_window.h" -#include <cstddef> +#include "ash/common/shell_window_ids.h" +#include "ash/shell.h" +#include "ui/aura/window.h" +#include "ui/events/gestures/gesture_recognizer.h" namespace chromeos { -LockWindow::LockWindow() : observer_(NULL), initially_focused_view_(NULL) { +LockWindow::LockWindow() { + ui::GestureRecognizer::Get()->CancelActiveTouchesExcept(nullptr); + + views::Widget::InitParams params( + views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); + params.delegate = this; + params.show_state = ui::SHOW_STATE_FULLSCREEN; + params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; + params.parent = + ash::Shell::GetContainer(ash::Shell::GetPrimaryRootWindow(), + ash::kShellWindowId_LockScreenContainer); + views::Widget::Init(params); + SetVisibilityAnimationTransition(views::Widget::ANIMATE_NONE); +} + +LockWindow::~LockWindow() {} + +void LockWindow::Grab() { + // We already have grab from the lock screen container, just call the ready + // callback immediately. + if (observer_) + observer_->OnLockWindowReady(); +} + +views::Widget* LockWindow::GetWidget() { + return this; +} + +const views::Widget* LockWindow::GetWidget() const { + return this; +} + +views::View* LockWindow::GetInitiallyFocusedView() { + return initially_focused_view_; } } // namespace chromeos
diff --git a/chrome/browser/chromeos/login/ui/lock_window.h b/chrome/browser/chromeos/login/ui/lock_window.h index b3076a2..9fbc0fe 100644 --- a/chrome/browser/chromeos/login/ui/lock_window.h +++ b/chrome/browser/chromeos/login/ui/lock_window.h
@@ -6,6 +6,8 @@ #define CHROME_BROWSER_CHROMEOS_LOGIN_UI_LOCK_WINDOW_H_ #include "base/macros.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" namespace views { class View; @@ -14,9 +16,8 @@ namespace chromeos { -// This is the interface which lock windows used for the WebUI screen locker -// implement. -class LockWindow { +// Shows the widget for the WebUI screen locker. +class LockWindow : public views::Widget, public views::WidgetDelegate { public: // This class provides an interface for the lock window to notify an observer // about its status. @@ -28,36 +29,35 @@ }; LockWindow(); + ~LockWindow() override; // Attempt to grab inputs on the webview, the actual view displaying the lock // screen WebView. - virtual void Grab() = 0; - - // Returns the actual widget for the lock window. - virtual views::Widget* GetWidget() = 0; + void Grab(); // Sets the observer class which is notified on lock window events. - void set_observer(Observer* observer) { - observer_ = observer; - } + void set_observer(Observer* observer) { observer_ = observer; } // Sets the view which should be initially focused. void set_initially_focused_view(views::View* view) { initially_focused_view_ = view; } - // Creates an instance of the platform specific lock window. - static LockWindow* Create(); - - protected: - // The observer's OnLockWindowReady method will be called when the lock - // window has finished all initialization. - Observer* observer_; - - // The view which should be initially focused. - views::View* initially_focused_view_; + // views::WidgetDelegate: + views::Widget* GetWidget() override; + const views::Widget* GetWidget() const override; private: + // views::WidgetDelegate: + views::View* GetInitiallyFocusedView() override; + + // The observer's OnLockWindowReady method will be called when the lock + // window has finished all initialization. + Observer* observer_ = nullptr; + + // The view which should be initially focused. + views::View* initially_focused_view_ = nullptr; + DISALLOW_COPY_AND_ASSIGN(LockWindow); };
diff --git a/chrome/browser/chromeos/login/ui/lock_window_aura.cc b/chrome/browser/chromeos/login/ui/lock_window_aura.cc deleted file mode 100644 index bff36c4..0000000 --- a/chrome/browser/chromeos/login/ui/lock_window_aura.cc +++ /dev/null
@@ -1,68 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/chromeos/login/ui/lock_window_aura.h" - -#include "ash/common/shell_window_ids.h" -#include "ash/shell.h" -#include "base/command_line.h" -#include "ui/aura/window.h" -#include "ui/aura/window_event_dispatcher.h" - -namespace chromeos { - -LockWindow* LockWindow::Create() { - LockWindowAura* lock_window = new LockWindowAura(); - // Cancel existing touch events when screen is locked. - ui::GestureRecognizer::Get()->CancelActiveTouchesExcept(nullptr); - return lock_window; -} - -//////////////////////////////////////////////////////////////////////////////// -// LockWindow implementation: -void LockWindowAura::Grab() { - // We already have grab from the lock screen container, just call the ready - // callback immediately. - if (observer_) - observer_->OnLockWindowReady(); -} - -views::Widget* LockWindowAura::GetWidget() { - return this; -} - -//////////////////////////////////////////////////////////////////////////////// -// views::WidgetDelegate implementation: -views::View* LockWindowAura::GetInitiallyFocusedView() { - return initially_focused_view_; -} - -const views::Widget* LockWindowAura::GetWidget() const { - return this; -} - -//////////////////////////////////////////////////////////////////////////////// -// LockWindowAura private: -LockWindowAura::LockWindowAura() { - Init(); -} - -LockWindowAura::~LockWindowAura() { -} - -void LockWindowAura::Init() { - views::Widget::InitParams params( - views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); - params.delegate = this; - params.show_state = ui::SHOW_STATE_FULLSCREEN; - params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; - // TODO(oshima): move the lock screen harness to ash. - params.parent = - ash::Shell::GetContainer(ash::Shell::GetPrimaryRootWindow(), - ash::kShellWindowId_LockScreenContainer); - views::Widget::Init(params); - SetVisibilityAnimationTransition(views::Widget::ANIMATE_NONE); -} - -} // namespace chromeos
diff --git a/chrome/browser/chromeos/login/ui/lock_window_aura.h b/chrome/browser/chromeos/login/ui/lock_window_aura.h deleted file mode 100644 index 548ae162..0000000 --- a/chrome/browser/chromeos/login/ui/lock_window_aura.h +++ /dev/null
@@ -1,42 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_UI_LOCK_WINDOW_AURA_H_ -#define CHROME_BROWSER_CHROMEOS_LOGIN_UI_LOCK_WINDOW_AURA_H_ - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "chrome/browser/chromeos/login/ui/lock_window.h" -#include "ui/views/widget/widget.h" -#include "ui/views/widget/widget_delegate.h" - -namespace chromeos { - -class LockWindowAura : public views::Widget, - public views::WidgetDelegate, - public LockWindow { - public: - // LockWindow implementation: - void Grab() override; - views::Widget* GetWidget() override; - - // views::WidgetDelegate implementation: - views::View* GetInitiallyFocusedView() override; - const views::Widget* GetWidget() const override; - - private: - friend class LockWindow; - - LockWindowAura(); - ~LockWindowAura() override; - - // Initialize the Aura lock window. - void Init(); - - DISALLOW_COPY_AND_ASSIGN(LockWindowAura); -}; - -} // namespace chromeos - -#endif // CHROME_BROWSER_CHROMEOS_LOGIN_UI_LOCK_WINDOW_AURA_H_
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_browsertest.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_browsertest.cc index 79386a8..ea2f17e7 100644 --- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_browsertest.cc +++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_browsertest.cc
@@ -792,7 +792,9 @@ wallpaper_manager_test_utils::kLargeGuestWallpaperColor)); } -IN_PROC_BROWSER_TEST_F(WallpaperManagerBrowserTest, SmallChildWallpaper) { +// Fails (crbug/657180) +IN_PROC_BROWSER_TEST_F(WallpaperManagerBrowserTest, + DISABLED_SmallChildWallpaper) { if (!ash::test::AshTestHelper::SupportsMultipleDisplays()) return; CreateCmdlineWallpapers(); @@ -805,7 +807,9 @@ wallpaper_manager_test_utils::kSmallChildWallpaperColor)); } -IN_PROC_BROWSER_TEST_F(WallpaperManagerBrowserTest, LargeChildWallpaper) { +// Fails (crbug/657180) +IN_PROC_BROWSER_TEST_F(WallpaperManagerBrowserTest, + DISABLED_LargeChildWallpaper) { if (!ash::test::AshTestHelper::SupportsMultipleDisplays()) return;
diff --git a/chrome/browser/command_updater.cc b/chrome/browser/command_updater.cc index 950a17e..5321e01 100644 --- a/chrome/browser/command_updater.cc +++ b/chrome/browser/command_updater.cc
@@ -73,8 +73,8 @@ if (command->enabled == enabled) return; // Nothing to do. command->enabled = enabled; - FOR_EACH_OBSERVER(CommandObserver, command->observers, - EnabledStateChangedForCommand(id, enabled)); + for (auto& observer : command->observers) + observer.EnabledStateChangedForCommand(id, enabled); } CommandUpdater::Command* CommandUpdater::GetCommand(int id, bool create) {
diff --git a/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc b/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc index 8c27f28..4beaf8cf 100644 --- a/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc +++ b/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
@@ -211,6 +211,8 @@ &Delegate::RemovePreference, delegate); d->RegisterHandler("clearPreferences", &Delegate::ClearPreferences, delegate); + d->RegisterHandlerWithCallback("reattach", + &Delegate::Reattach, delegate); d->RegisterHandler("readyForTest", &Delegate::ReadyForTest, delegate); return d;
diff --git a/chrome/browser/devtools/devtools_embedder_message_dispatcher.h b/chrome/browser/devtools/devtools_embedder_message_dispatcher.h index 141392fa..fd8665b 100644 --- a/chrome/browser/devtools/devtools_embedder_message_dispatcher.h +++ b/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
@@ -88,6 +88,7 @@ virtual void SendJsonRequest(const DispatchCallback& callback, const std::string& browser_id, const std::string& url) = 0; + virtual void Reattach(const DispatchCallback& callback) = 0; virtual void ReadyForTest() = 0; };
diff --git a/chrome/browser/devtools/devtools_ui_bindings.cc b/chrome/browser/devtools/devtools_ui_bindings.cc index fd3f0dd..4f8473b3 100644 --- a/chrome/browser/devtools/devtools_ui_bindings.cc +++ b/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -402,7 +402,7 @@ delegate_(new DefaultBindingsDelegate(web_contents_)), devices_updates_enabled_(false), frontend_loaded_(false), - reattaching_(false), + reloading_(false), weak_factory_(this) { g_instances.Get().push_back(this); frontend_contents_observer_.reset(new FrontendWebContentsObserver(this)); @@ -856,6 +856,13 @@ update.Get()->Clear(); } +void DevToolsUIBindings::Reattach(const DispatchCallback& callback) { + DCHECK(agent_host_.get()); + agent_host_->DetachClient(this); + agent_host_->AttachClient(this); + callback.Run(nullptr); +} + void DevToolsUIBindings::ReadyForTest() { delegate_->ReadyForTest(); } @@ -1106,9 +1113,10 @@ agent_host_->ForceAttachClient(this); } -void DevToolsUIBindings::Reattach() { +void DevToolsUIBindings::Reload() { DCHECK(agent_host_.get()); - reattaching_ = true; + reloading_ = true; + web_contents_->GetController().Reload(false); } void DevToolsUIBindings::Detach() { @@ -1145,9 +1153,9 @@ } void DevToolsUIBindings::DocumentAvailableInMainFrame() { - if (!reattaching_) + if (!reloading_) return; - reattaching_ = false; + reloading_ = false; agent_host_->DetachClient(this); agent_host_->AttachClient(this); }
diff --git a/chrome/browser/devtools/devtools_ui_bindings.h b/chrome/browser/devtools/devtools_ui_bindings.h index 8888234..22939c4 100644 --- a/chrome/browser/devtools/devtools_ui_bindings.h +++ b/chrome/browser/devtools/devtools_ui_bindings.h
@@ -76,7 +76,7 @@ const base::Value* arg2, const base::Value* arg3); void AttachTo(const scoped_refptr<content::DevToolsAgentHost>& agent_host); - void Reattach(); + void Reload(); void Detach(); bool IsAttachedTo(content::DevToolsAgentHost* agent_host); @@ -145,6 +145,7 @@ const std::string& value) override; void RemovePreference(const std::string& name) override; void ClearPreferences() override; + void Reattach(const DispatchCallback& callback) override; void ReadyForTest() override; // net::URLFetcherDelegate overrides. @@ -221,7 +222,7 @@ bool devices_updates_enabled_; bool frontend_loaded_; - bool reattaching_; + bool reloading_; std::unique_ptr<DevToolsTargetsUIHandler> remote_targets_handler_; std::unique_ptr<PortForwardingStatusSerializer> port_status_serializer_; PrefChangeRegistrar pref_change_registrar_;
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc index 0f93220c..f8903d9 100644 --- a/chrome/browser/devtools/devtools_window.cc +++ b/chrome/browser/devtools/devtools_window.cc
@@ -943,11 +943,7 @@ return inspected_web_contents ? inspected_web_contents->OpenURL(params) : NULL; } - - bindings_->Reattach(); - - content::NavigationController::LoadURLParams load_url_params(params.url); - main_web_contents_->GetController().LoadURLWithParams(load_url_params); + bindings_->Reload(); return main_web_contents_; }
diff --git a/chrome/browser/media/android/router/media_router_android.cc b/chrome/browser/media/android/router/media_router_android.cc index 62f8fee20..f7d5a11 100644 --- a/chrome/browser/media/android/router/media_router_android.cc +++ b/chrome/browser/media/android/router/media_router_android.cc
@@ -355,8 +355,8 @@ auto it = sinks_observers_.find(source_urn); if (it != sinks_observers_.end()) { // TODO(imcheng): Pass origins to OnSinksUpdated (crbug.com/594858). - FOR_EACH_OBSERVER(MediaSinksObserver, *it->second, - OnSinksUpdated(sinks_converted, std::vector<GURL>())); + for (auto& observer : *it->second) + observer.OnSinksUpdated(sinks_converted, std::vector<GURL>()); } } @@ -385,8 +385,8 @@ route_requests_.Remove(jroute_request_id); active_routes_.push_back(route); - FOR_EACH_OBSERVER(MediaRoutesObserver, routes_observers_, - OnRoutesUpdated(active_routes_, std::vector<MediaRoute::Id>())); + for (auto& observer : routes_observers_) + observer.OnRoutesUpdated(active_routes_, std::vector<MediaRoute::Id>()); } void MediaRouterAndroid::OnRouteRequestError( @@ -419,8 +419,8 @@ break; } - FOR_EACH_OBSERVER(MediaRoutesObserver, routes_observers_, - OnRoutesUpdated(active_routes_, std::vector<MediaRoute::Id>())); + for (auto& observer : routes_observers_) + observer.OnRoutesUpdated(active_routes_, std::vector<MediaRoute::Id>()); NotifyPresentationConnectionStateChange( route_id, content::PRESENTATION_CONNECTION_STATE_TERMINATED); } @@ -462,8 +462,8 @@ std::vector<RouteMessage> messages(1); messages.front().type = RouteMessage::TEXT; messages.front().text = ConvertJavaStringToUTF8(env, jmessage); - FOR_EACH_OBSERVER(RouteMessageObserver, *observer_list, - OnMessagesReceived(messages)); + for (auto& observer : *observer_list) + observer.OnMessagesReceived(messages); } } // namespace media_router
diff --git a/chrome/browser/media/router/issue_manager.cc b/chrome/browser/media/router/issue_manager.cc index d3cb9d4..53d74c8c 100644 --- a/chrome/browser/media/router/issue_manager.cc +++ b/chrome/browser/media/router/issue_manager.cc
@@ -95,8 +95,8 @@ const Issue* new_top_issue = nullptr; if (issues_.empty()) { - FOR_EACH_OBSERVER(IssuesObserver, issues_observers_, - OnIssueUpdated(new_top_issue)); + for (auto& observer : issues_observers_) + observer.OnIssueUpdated(new_top_issue); return; } @@ -114,8 +114,8 @@ // If we've found a new top issue, then report it via the observer. if (new_top_issue->id() != top_issue_id_) { top_issue_id_ = new_top_issue->id(); - FOR_EACH_OBSERVER(IssuesObserver, issues_observers_, - OnIssueUpdated(new_top_issue)); + for (auto& observer : issues_observers_) + observer.OnIssueUpdated(new_top_issue); } }
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc index 0155bef..9517982 100644 --- a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc +++ b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
@@ -201,9 +201,10 @@ DVLOG_WITH_INSTANCE(1) << "Received sink list without any active observers: " << media_source; } else { - FOR_EACH_OBSERVER( - MediaSinksObserver, sinks_query->observers, - OnSinksUpdated(sinks_query->cached_sink_list, sinks_query->origins)); + for (auto& observer : sinks_query->observers) { + observer.OnSinksUpdated(sinks_query->cached_sink_list, + sinks_query->origins); + } } } @@ -227,9 +228,8 @@ for (size_t i = 0; i < routes.size(); ++i) routes_converted.push_back(routes[i].To<MediaRoute>()); - FOR_EACH_OBSERVER( - MediaRoutesObserver, it->second->observers, - OnRoutesUpdated(routes_converted, joinable_route_ids)); + for (auto& observer : it->second->observers) + observer.OnRoutesUpdated(routes_converted, joinable_route_ids); } void MediaRouterMojoImpl::RouteResponseReceived( @@ -710,8 +710,8 @@ return; } - FOR_EACH_OBSERVER(RouteMessageObserver, *observer_list, - OnMessagesReceived(messages)); + for (auto& observer : *observer_list) + observer.OnMessagesReceived(messages); } void MediaRouterMojoImpl::OnSinkAvailabilityUpdated(
diff --git a/chrome/browser/media/router/presentation_service_delegate_impl.cc b/chrome/browser/media/router/presentation_service_delegate_impl.cc index 415974da..0a40a5b 100644 --- a/chrome/browser/media/router/presentation_service_delegate_impl.cc +++ b/chrome/browser/media/router/presentation_service_delegate_impl.cc
@@ -622,9 +622,8 @@ return; default_presentation_request_.reset(); - FOR_EACH_OBSERVER( - PresentationServiceDelegateImpl::DefaultPresentationRequestObserver, - default_presentation_request_observers_, OnDefaultPresentationRemoved()); + for (auto& observer : default_presentation_request_observers_) + observer.OnDefaultPresentationRemoved(); } bool PresentationFrameManager::IsMainFrame( @@ -641,10 +640,8 @@ default_presentation_request_.reset( new PresentationRequest(default_presentation_request)); - FOR_EACH_OBSERVER( - PresentationServiceDelegateImpl::DefaultPresentationRequestObserver, - default_presentation_request_observers_, - OnDefaultPresentationChanged(*default_presentation_request_)); + for (auto& observer : default_presentation_request_observers_) + observer.OnDefaultPresentationChanged(*default_presentation_request_); } void PresentationFrameManager::SetMediaRouterForTest(MediaRouter* router) {
diff --git a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc index 8ea8d36cb..e4177e3 100644 --- a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc +++ b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc
@@ -322,14 +322,14 @@ void MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread() { MediaStreamDevices devices = GetAudioCaptureDevices(); - FOR_EACH_OBSERVER(Observer, observers_, - OnUpdateAudioDevices(devices)); + for (auto& observer : observers_) + observer.OnUpdateAudioDevices(devices); } void MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread() { MediaStreamDevices devices = GetVideoCaptureDevices(); - FOR_EACH_OBSERVER(Observer, observers_, - OnUpdateVideoDevices(devices)); + for (auto& observer : observers_) + observer.OnUpdateVideoDevices(devices); } void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread( @@ -358,19 +358,18 @@ } #endif - FOR_EACH_OBSERVER(Observer, observers_, - OnRequestUpdate(render_process_id, - render_frame_id, - stream_type, - state)); + for (auto& observer : observers_) { + observer.OnRequestUpdate(render_process_id, render_frame_id, stream_type, + state); + } } void MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread( int render_process_id, int render_frame_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - FOR_EACH_OBSERVER(Observer, observers_, - OnCreatingAudioStream(render_process_id, render_frame_id)); + for (auto& observer : observers_) + observer.OnCreatingAudioStream(render_process_id, render_frame_id); } bool MediaCaptureDevicesDispatcher::IsInsecureCapturingInProgress(
diff --git a/chrome/browser/media_galleries/media_galleries_preferences.cc b/chrome/browser/media_galleries/media_galleries_preferences.cc index a32a4bb..db859f5 100644 --- a/chrome/browser/media_galleries/media_galleries_preferences.cc +++ b/chrome/browser/media_galleries/media_galleries_preferences.cc
@@ -638,9 +638,8 @@ InitFromPrefs(); MediaGalleryPrefId pref_id; if (GetPrefId(*dict, &pref_id)) { - FOR_EACH_OBSERVER(GalleryChangeObserver, - gallery_change_observers_, - OnGalleryInfoUpdated(this, pref_id)); + for (auto& observer : gallery_change_observers_) + observer.OnGalleryInfoUpdated(this, pref_id); } return true; } @@ -997,8 +996,8 @@ update.reset(); InitFromPrefs(); - FOR_EACH_OBSERVER(GalleryChangeObserver, gallery_change_observers_, - OnGalleryInfoUpdated(this, *pref_id_it)); + for (auto& observer : gallery_change_observers_) + observer.OnGalleryInfoUpdated(this, *pref_id_it); return *pref_id_it; } @@ -1029,9 +1028,8 @@ list->Append(CreateGalleryPrefInfoDictionary(gallery_info)); } InitFromPrefs(); - FOR_EACH_OBSERVER(GalleryChangeObserver, - gallery_change_observers_, - OnGalleryAdded(this, gallery_info.pref_id)); + for (auto& observer : gallery_change_observers_) + observer.OnGalleryAdded(this, gallery_info.pref_id); return gallery_info.pref_id; } @@ -1105,9 +1103,8 @@ for (std::vector<MediaGalleryPrefId>::iterator iter = pref_ids.begin(); iter != pref_ids.end(); ++iter) { - FOR_EACH_OBSERVER(GalleryChangeObserver, - gallery_change_observers_, - OnGalleryInfoUpdated(this, *iter)); + for (auto& observer : gallery_change_observers_) + observer.OnGalleryInfoUpdated(this, *iter); } } @@ -1181,9 +1178,8 @@ update.reset(NULL); // commits the update. InitFromPrefs(); - FOR_EACH_OBSERVER(GalleryChangeObserver, - gallery_change_observers_, - OnGalleryRemoved(this, id)); + for (auto& observer : gallery_change_observers_) + observer.OnGalleryRemoved(this, id); return; } } @@ -1285,14 +1281,13 @@ if (!SetGalleryPermissionInPrefs(extension.id(), pref_id, has_permission)) return false; } - if (has_permission) - FOR_EACH_OBSERVER(GalleryChangeObserver, - gallery_change_observers_, - OnPermissionAdded(this, extension.id(), pref_id)); - else - FOR_EACH_OBSERVER(GalleryChangeObserver, - gallery_change_observers_, - OnPermissionRemoved(this, extension.id(), pref_id)); + if (has_permission) { + for (auto& observer : gallery_change_observers_) + observer.OnPermissionAdded(this, extension.id(), pref_id); + } else { + for (auto& observer : gallery_change_observers_) + observer.OnPermissionRemoved(this, extension.id(), pref_id); + } return true; }
diff --git a/chrome/browser/notifications/notification_display_service_factory.cc b/chrome/browser/notifications/notification_display_service_factory.cc index 1caf6574..8b40bcd 100644 --- a/chrome/browser/notifications/notification_display_service_factory.cc +++ b/chrome/browser/notifications/notification_display_service_factory.cc
@@ -11,7 +11,7 @@ #include "chrome/browser/notifications/notification_ui_manager.h" #include "chrome/browser/profiles/incognito_helpers.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/chrome_switches.h" +#include "chrome/common/chrome_features.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #if defined(OS_ANDROID) || defined(OS_MACOSX) @@ -39,8 +39,8 @@ // Selection of the implementation works as follows: // - Android always uses the NativeNotificationDisplayService. // - Mac uses the MessageCenterDisplayService by default, but can use the -// NativeNotificationDisplayService by using the chrome://flags or the -// --enable-native-notifications command line flag. +// NativeNotificationDisplayService by using the chrome://flags or via +// the --enable-features=NativeNotifications command line flag. // - All other platforms always use the MessageCenterDisplayService. KeyedService* NotificationDisplayServiceFactory::BuildServiceInstanceFor( content::BrowserContext* context) const { @@ -49,8 +49,7 @@ Profile::FromBrowserContext(context), g_browser_process->notification_platform_bridge()); #elif defined(OS_MACOSX) - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableNativeNotifications)) { + if (base::FeatureList::IsEnabled(features::kNativeNotifications)) { return new NativeNotificationDisplayService( Profile::FromBrowserContext(context), g_browser_process->notification_platform_bridge());
diff --git a/chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc b/chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc index 384a4c0b..bb73b4fe 100644 --- a/chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc +++ b/chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc
@@ -6,15 +6,19 @@ #include "base/command_line.h" #include "base/strings/utf_string_conversions.h" +#include "chrome/browser/chrome_content_browser_client.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/interactive_test_utils.h" #include "chrome/test/base/ui_test_utils.h" +#include "content/public/browser/browser_message_filter.h" +#include "content/public/browser/content_browser_client.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents.h" +#include "content/public/common/content_client.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/content_browser_test_utils.h" #include "content/public/test/test_utils.h" @@ -330,7 +334,8 @@ // static // Adds an <input> field to a given frame by executing javascript code. // The input can be added as the first element or the last element of - // |document.body|. + // |document.body|. The text range defined by |selection_range| will be + // marked. static void AddInputFieldToFrame(content::RenderFrameHost* rfh, const std::string& type, const std::string& value, @@ -948,3 +953,180 @@ EXPECT_EQ("", result); } #endif + +// Ideally, the following code + test should be live in +// 'site_per_process_mac_browsertest.mm'. However, the test +// 'LookUpStringForRangeRoutesToFocusedWidget' relies on an override in +// ContentBrowserClient to register its filters in time. In content shell, we +// cannot have two instances of ShellContentBrowserClient (due to a DCHECK in +// the ctor). Therefore, we put the test here to use ChromeContentBrowserClient +// which does not have the same singleton constraint. +#if defined(OS_MACOSX) +// An observer of number of open windows. +class WindowCountObserver { + public: + explicit WindowCountObserver(size_t lower_limit) : limit_(lower_limit) {} + ~WindowCountObserver() {} + + // Keep polling the count of open windows until the number exceeds |limit_|. + void WaitForLimitOrMore() { + size_t current_count = content::GetOpenNSWindowsCount(); + if (current_count >= limit_) + return; + + message_loop_runner_ = new content::MessageLoopRunner(); + message_loop_runner_->Run(); + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&WindowCountObserver::CheckWindowCount, + base::Unretained(this))); + } + + private: + void CheckWindowCount() { + size_t current_count = content::GetOpenNSWindowsCount(); + if (current_count >= limit_) { + message_loop_runner_->Quit(); + return; + } + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&WindowCountObserver::CheckWindowCount, + base::Unretained(this))); + } + + size_t limit_; + scoped_refptr<content::MessageLoopRunner> message_loop_runner_; + + DISALLOW_COPY_AND_ASSIGN(WindowCountObserver); +}; + +// The original TextInputClientMessageFilter is added during the initialization +// phase of RenderProcessHost. The only chance we have to add the test filter +// (so that it can receive the TextInputClientMac incoming IPC messages) is +// during the call to RenderProcessWillLaunch() on ContentBrowserClient. This +// class provides that for testing. +class TestBrowserClient : public ChromeContentBrowserClient { + public: + TestBrowserClient() {} + ~TestBrowserClient() override {} + + // ContentBrowserClient overrides. + void RenderProcessWillLaunch( + content::RenderProcessHost* process_host) override { + ChromeContentBrowserClient::RenderProcessWillLaunch(process_host); + filters_.push_back( + new content::TestTextInputClientMessageFilter(process_host)); + } + + // Retrieves the registered filter for the given RenderProcessHost. It will + // return false if the RenderProcessHost was initialized while a different + // instance of ContentBrowserClient was in action. + scoped_refptr<content::TestTextInputClientMessageFilter> + GetTextInputClientMessageFilterForProcess( + content::RenderProcessHost* process_host) const { + for (auto filter : filters_) { + if (filter->process() == process_host) + return filter; + } + return nullptr; + } + + private: + std::vector<scoped_refptr<content::TestTextInputClientMessageFilter>> + filters_; + + DISALLOW_COPY_AND_ASSIGN(TestBrowserClient); +}; + +// This test verifies that requests for dictionary lookup based on selection +// range are routed to the focused RenderWidgetHost. +IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest, + LookUpStringForRangeRoutesToFocusedWidget) { + // TestBrowserClient needs to replace the ChromeContenBrowserClient after most + // things are initialized but before the WebContents is created. Here we make + // that happen by creating a new WebContents in a new tab. But before the test + // exits, we must destroy the contents and replace the old + // ContentBrowserClient because the original WebContents and the new one have + // been initialized with the original ContentBrowserClient and the new + // TestBrowserClient, respectively. + TestBrowserClient browser_client; + content::ContentBrowserClient* old_browser_client = + content::SetBrowserClientForTesting(&browser_client); + + content::WebContents* new_contents = + content::WebContents::Create(content::WebContents::CreateParams( + active_contents()->GetBrowserContext(), nullptr)); + browser()->tab_strip_model()->InsertWebContentsAt(1, new_contents, + TabStripModel::ADD_ACTIVE); + EXPECT_EQ(active_contents(), new_contents); + + // Starting the test body. + CreateIframePage("a(b)"); + std::vector<content::RenderFrameHost*> frames{GetFrame(IndexVector{}), + GetFrame(IndexVector{0})}; + std::vector<content::RenderWidgetHostView*> views; + for (auto frame : frames) + views.push_back(frame->GetView()); + std::vector<std::string> values{"main frame", "child frame"}; + + // Adding some field with text to each frame so that we can later query for + // dictionary lookup. + for (size_t i = 0; i < frames.size(); ++i) + AddInputFieldToFrame(frames[i], "text", values[i], true); + + std::string result; + // Recording window count now so that our WindowCountObserver will detect the + // popup window. + size_t current_window_count = content::GetOpenNSWindowsCount(); + + // Ensure we have both focus and selected text inside the main frame. + EXPECT_TRUE( + ExecuteScript(frames[0], "document.querySelector('input').focus();")); + + // Request for the dictionary lookup and intercept the word on its way back. + // The request is always on the tab's view which is a RenderWidgetHostViewMac. + content::AskForLookUpDictionaryForRange(views[0], gfx::Range(0, 4)); + + // Wait until the result comes back. + auto root_filter = browser_client.GetTextInputClientMessageFilterForProcess( + frames[0]->GetProcess()); + EXPECT_TRUE(root_filter); + root_filter->WaitForStringFromRange(); + + EXPECT_EQ("main", root_filter->string_from_range()); + + // Wait for the popup to appear to make sure TextInputClientMac has consumed + // the reply handler for the previous request. + WindowCountObserver(current_window_count).WaitForLimitOrMore(); + + // Ensure we have both focus and selected text inside the child frame. + EXPECT_TRUE( + ExecuteScript(frames[1], "document.querySelector('input').focus();")); + + // Record window count again for the popup observer. + current_window_count = content::GetOpenNSWindowsCount(); + + // Make another request. + content::AskForLookUpDictionaryForRange(views[0], gfx::Range(0, 5)); + + // Wait until the result comes back. + auto child_filter = browser_client.GetTextInputClientMessageFilterForProcess( + frames[1]->GetProcess()); + child_filter->WaitForStringFromRange(); + + EXPECT_EQ("child", child_filter->string_from_range()); + + // Wait for the popup to appear to make sure TextInputClientMac has consumed + // the reply handler for the previous request. + WindowCountObserver(current_window_count).WaitForLimitOrMore(); + // Test ends here. The rest is cleanup. + + // Closing this WebContents while we still hold on to our TestBrowserClient. + EXPECT_TRUE(browser()->tab_strip_model()->CloseWebContentsAt( + 1, TabStripModel::CLOSE_USER_GESTURE)); + + // For the cleanup of the original WebContents in tab index 0. + content::SetBrowserClientForTesting(old_browser_client); +} +#endif
diff --git a/chrome/browser/resources/chromeos/arc_support/background.js b/chrome/browser/resources/chromeos/arc_support/background.js index 9b3e788..8dbfcc8 100644 --- a/chrome/browser/resources/chromeos/arc_support/background.js +++ b/chrome/browser/resources/chromeos/arc_support/background.js
@@ -167,7 +167,7 @@ * Called when the "Learn More" link is clicked. */ onLearnMoreLinkClicked() { - showLearnMoreOverlay(this.learnMoreContent_); + showTextOverlay(this.learnMoreContent_); } }; @@ -366,7 +366,7 @@ return; } - hideLearnMoreOverlay(); + hideOverlay(); var doc = appWindow.contentWindow.document; var pages = doc.getElementsByClassName('section'); var sendFeedbackElement = doc.getElementById('button-send-feedback'); @@ -409,24 +409,47 @@ } /** - * Sets learn more content text and shows it as overlay dialog. - * @param {string} content HTML formatted text to show. + * Shows overlay dialog and required content. + * @param {string} overlayClass Defines which content to show, 'overlay-url' for + * webview based content and 'overlay-text' for + * simple text view. */ -function showLearnMoreOverlay(content) { +function showOverlay(overlayClass) { var doc = appWindow.contentWindow.document; - var learnMoreContainer = doc.getElementById('learn-more-container'); - var learnMoreContent = doc.getElementById('learn-more-content'); - learnMoreContent.innerHTML = content; - learnMoreContainer.hidden = false; + var overlayContainer = doc.getElementById('overlay-container'); + overlayContainer.className = 'overlay ' + overlayClass; + overlayContainer.hidden = false; } /** - * Hides learn more overlay dialog. + * Opens overlay dialog and shows formatted text content there. + * @param {string} content HTML formatted text to show. */ -function hideLearnMoreOverlay() { +function showTextOverlay(content) { var doc = appWindow.contentWindow.document; - var learnMoreContainer = doc.getElementById('learn-more-container'); - learnMoreContainer.hidden = true; + var textContent = doc.getElementById('overlay-text-content'); + textContent.innerHTML = content; + showOverlay('overlay-text'); +} + +/** + * Opens overlay dialog and shows external URL there. + * @param {string} url Target URL to open in overlay dialog. + */ +function showURLOverlay(url) { + var doc = appWindow.contentWindow.document; + var overlayWebview = doc.getElementById('overlay-url'); + overlayWebview.src = url; + showOverlay('overlay-url'); +} + +/** + * Hides overlay dialog. + */ +function hideOverlay() { + var doc = appWindow.contentWindow.document; + var overlayContainer = doc.getElementById('overlay-container'); + overlayContainer.hidden = true; } /** @@ -600,10 +623,10 @@ // webview is not allowed to open links in the new window. Hook these events - // and open links in context of main page. + // and open links in overlay dialog. termsView.addEventListener('newwindow', function(event) { event.preventDefault(); - chrome.browser.openTab({'url': event.targetUrl}, function() {}); + showURLOverlay(event.targetUrl); }); var onAgree = function() { @@ -641,13 +664,12 @@ doc.getElementById('button-retry').addEventListener('click', onRetry); doc.getElementById('button-send-feedback') .addEventListener('click', onSendFeedback); - doc.getElementById('learn-more-close').addEventListener( - 'click', hideLearnMoreOverlay); + doc.getElementById('overlay-close').addEventListener('click', hideOverlay); - var overlay = doc.getElementById('learn-more-container'); + var overlay = doc.getElementById('overlay-container'); appWindow.contentWindow.cr.ui.overlay.setupOverlay(overlay); appWindow.contentWindow.cr.ui.overlay.globalInitialization(); - overlay.addEventListener('cancelOverlay', hideLearnMoreOverlay); + overlay.addEventListener('cancelOverlay', hideOverlay); connectPort(); };
diff --git a/chrome/browser/resources/chromeos/arc_support/main.css b/chrome/browser/resources/chromeos/arc_support/main.css index a7704c9..e2226be 100644 --- a/chrome/browser/resources/chromeos/arc_support/main.css +++ b/chrome/browser/resources/chromeos/arc_support/main.css
@@ -87,11 +87,34 @@ width: 960px; } +.overlay-text .page { + width: 480px; +} + +.overlay-text webview { + display: none; +} + +.overlay-url .page { + width: 640px; +} + +.overlay-url span { + display: none; +} + +.overlay-url webview { + display: block; + height: 300px; + margin: 24px 8px 8px 8px; + padding: 0; + width: 100%; +} + .page { font-family: 'Roboto'; font-size: 13px; text-align: left; - width: 480px; } .scrollable {
diff --git a/chrome/browser/resources/chromeos/arc_support/main.html b/chrome/browser/resources/chromeos/arc_support/main.html index 7291c940..bba6c8d 100644 --- a/chrome/browser/resources/chromeos/arc_support/main.html +++ b/chrome/browser/resources/chromeos/arc_support/main.html
@@ -144,16 +144,17 @@ </div> </div> </div> - <div id="learn-more-container" class="overlay" hidden> + <div id="overlay-container" class="overlay" hidden> <div class="page"> <div class="close-button"></div> <div class="content-area"> - <span id="learn-more-content"></span> + <span id="overlay-text-content"></span> + <webview id="overlay-url"></webview> </div> <div class="action-area"> <div class="action-area-right"> <div class="button-strip"> - <button id="learn-more-close" i18n-content="learnMoreClose"> + <button id="overlay-close" i18n-content="overlayClose"> </button> </div> </div>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/i_search_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/i_search_test.extjs index fe4fe4b3..27ff448 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/i_search_test.extjs +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/i_search_test.extjs
@@ -64,7 +64,7 @@ } }; -TEST_F('ISearchTest', 'Simple', function() { +TEST_F('ISearchTest', 'DISABLED_Simple', function() { this.runWithLoadedTree(this.linksAndHeadingsDoc, function(rootNode) { var handler = new FakeISearchHandler(this); var search = new ISearch(rootNode);
diff --git a/chrome/browser/resources/chromeos/quick_unlock/pin_keyboard.html b/chrome/browser/resources/chromeos/quick_unlock/pin_keyboard.html index 83e6a69e..96e3523 100644 --- a/chrome/browser/resources/chromeos/quick_unlock/pin_keyboard.html +++ b/chrome/browser/resources/chromeos/quick_unlock/pin_keyboard.html
@@ -44,7 +44,7 @@ .digit-button { align-items: center; background: none; - border-radius: 100px; + border-radius: 0; box-sizing: border-box; color: #000; display: flex; @@ -52,12 +52,12 @@ font-size: 18px; height: 48px; justify-content: center; - margin: 0 6px; + margin: 0; min-height: 48px; min-width: 48px; opacity: 0.87; - padding: 15px; - width: 48px; + padding: 15px 21px; + width: 60px; } [hidden=true] { @@ -72,9 +72,29 @@ margin-top: 6px; } + .backspace-button-container { + position: relative; + } + + .backspace-button-container paper-ripple { + position: absolute; + top: 0; + } + + paper-ripple { + border-radius: 100px; + color: #000; + height: 48px; + left: 6px; + width: 48px; + } + .digit-button.backspace-button { + left: 0; opacity: var(--dark-primary-opacity); padding: 14px; + position: absolute; + top: 0; } .digit-button.backspace-button:not([has-content]) { @@ -159,50 +179,74 @@ </div> <hr> <div class="row keyboard"> - <paper-button class="digit-button" on-tap="onNumberTap_" value="1"> + <paper-button class="digit-button" on-tap="onNumberTap_" value="1" + noink> <inner-text>$i18n{pinKeyboard1}</inner-text> + <paper-ripple> </paper-button> - <paper-button class="digit-button" on-tap="onNumberTap_" value="2"> + <paper-button class="digit-button" on-tap="onNumberTap_" value="2" + noink> <inner-text>$i18n{pinKeyboard2}</inner-text> + <paper-ripple> </paper-button> - <paper-button class="digit-button" on-tap="onNumberTap_" value="3"> + <paper-button class="digit-button" on-tap="onNumberTap_" value="3" + noink> <inner-text>$i18n{pinKeyboard3}</inner-text> + <paper-ripple> </paper-button> </div> <div class="row keyboard"> - <paper-button class="digit-button" on-tap="onNumberTap_" value="4"> + <paper-button class="digit-button" on-tap="onNumberTap_" value="4" + noink> <inner-text>$i18n{pinKeyboard4}</inner-text> + <paper-ripple> </paper-button> - <paper-button class="digit-button" on-tap="onNumberTap_" value="5"> + <paper-button class="digit-button" on-tap="onNumberTap_" value="5" + noink> <inner-text>$i18n{pinKeyboard5}</inner-text> + <paper-ripple> </paper-button> - <paper-button class="digit-button" on-tap="onNumberTap_" value="6"> + <paper-button class="digit-button" on-tap="onNumberTap_" value="6" + noink> <inner-text>$i18n{pinKeyboard6}</inner-text> + <paper-ripple> </paper-button> </div> <div class="row keyboard"> - <paper-button class="digit-button" on-tap="onNumberTap_" value="7"> + <paper-button class="digit-button" on-tap="onNumberTap_" value="7" + noink> <inner-text>$i18n{pinKeyboard7}</inner-text> + <paper-ripple> </paper-button> - <paper-button class="digit-button" on-tap="onNumberTap_" value="8"> + <paper-button class="digit-button" on-tap="onNumberTap_" value="8" + noink> <inner-text>$i18n{pinKeyboard8}</inner-text> + <paper-ripple> </paper-button> - <paper-button class="digit-button" on-tap="onNumberTap_" value="9"> + <paper-button class="digit-button" on-tap="onNumberTap_" value="9" + noink> <inner-text>$i18n{pinKeyboard9}</inner-text> + <paper-ripple> </paper-button> </div> <div class="row keyboard bottom-row"> <div class="digit-button"></div> - <paper-button class="digit-button" on-tap="onNumberTap_" value="0"> + <paper-button class="digit-button" on-tap="onNumberTap_" value="0" + noink> <inner-text>$i18n{pinKeyboard0}</inner-text> + <paper-ripple> </paper-button> - <paper-icon-button class="digit-button backspace-button" - has-content$="[[hasInput_(value)]]" - icon="pin-keyboard:backspace" - on-pointerdown="onBackspacePointerDown_" - on-pointerout="onBackspacePointerOut_" - on-pointerup="onBackspacePointerUp_"> - </paper-icon-button> + <div class="backspace-button-container"> + <paper-icon-button class="digit-button backspace-button" + has-content$="[[hasInput_(value)]]" + icon="pin-keyboard:backspace" + on-pointerdown="onBackspacePointerDown_" + on-pointerout="onBackspacePointerOut_" + on-pointerup="onBackspacePointerUp_" + noink> + </paper-icon-button> + <paper-ripple> + </div> </div> </div> </div>
diff --git a/chrome/browser/resources/md_extensions/error_page.js b/chrome/browser/resources/md_extensions/error_page.js index 18a03e7527..10fa7848 100644 --- a/chrome/browser/resources/md_extensions/error_page.js +++ b/chrome/browser/resources/md_extensions/error_page.js
@@ -78,10 +78,13 @@ }, /** - * @param {!{model:Object}} e + * @param {!Event} event * @private */ - onDeleteErrorTap_: function(e) { + onDeleteErrorTap_: function(event) { + // TODO(devlin): It would be cleaner if we could cast this to a + // PolymerDomRepeatEvent-type thing, but that doesn't exist yet. + var e = /** @type {!{model:Object}} */(event); this.delegate.deleteErrors(this.data.id, [e.model.item.id]); }, });
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_browser_proxy.html b/chrome/browser/resources/settings/appearance_page/appearance_browser_proxy.html index 4b8d2b5b..b7dd3ef 100644 --- a/chrome/browser/resources/settings/appearance_page/appearance_browser_proxy.html +++ b/chrome/browser/resources/settings/appearance_page/appearance_browser_proxy.html
@@ -1,2 +1,3 @@ <link rel="href" src="chrome://resources/html/cr.html"> +<link rel="href" src="chrome://resources/html/load_time_data.html"> <script src="/appearance_page/appearance_browser_proxy.js"></script>
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_browser_proxy.js b/chrome/browser/resources/settings/appearance_page/appearance_browser_proxy.js index d5df599..e9cd6ec 100644 --- a/chrome/browser/resources/settings/appearance_page/appearance_browser_proxy.js +++ b/chrome/browser/resources/settings/appearance_page/appearance_browser_proxy.js
@@ -13,6 +13,9 @@ */ getThemeInfo: assertNotReached, + /** @return {boolean} Whether the current profile is supervised. */ + isSupervised: assertNotReached, + <if expr="chromeos"> openWallpaperManager: assertNotReached, </if> @@ -40,6 +43,11 @@ }); }, + /** @override */ + isSupervised: function() { + return loadTimeData.getBoolean('isSupervised'); + }, + <if expr="chromeos"> /** @override */ openWallpaperManager: function() {
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_page.html b/chrome/browser/resources/settings/appearance_page/appearance_page.html index 63b68c7..2427d068 100644 --- a/chrome/browser/resources/settings/appearance_page/appearance_page.html +++ b/chrome/browser/resources/settings/appearance_page/appearance_page.html
@@ -67,7 +67,8 @@ </template> </if> <if expr="is_linux and not chromeos"> - <div class="secondary-action"> + <div class="secondary-action" hidden="[[!showThemesSecondary_( + prefs.extensions.theme.id.value, useSystemTheme_)]]"> <template is="dom-if" if="[[showUseClassic_( prefs.extensions.theme.id.value, useSystemTheme_)]]" restamp> <paper-button id="useDefault" on-tap="onUseDefaultTap_"
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_page.js b/chrome/browser/resources/settings/appearance_page/appearance_page.js index f94507d..49f9fc1a 100644 --- a/chrome/browser/resources/settings/appearance_page/appearance_page.js +++ b/chrome/browser/resources/settings/appearance_page/appearance_page.js
@@ -188,7 +188,19 @@ * @private */ showUseSystem_: function(themeId, useSystemTheme) { - return !!themeId || !useSystemTheme; + return (!!themeId || !useSystemTheme) && !this.browserProxy_.isSupervised(); + }, + + /** + * @param {string} themeId + * @param {boolean} useSystemTheme + * @return {boolean} Whether to show the secondary area where "USE CLASSIC" + * and "USE GTK+" buttons live. + * @private + */ + showThemesSecondary_: function(themeId, useSystemTheme) { + return this.showUseClassic_(themeId, useSystemTheme) || + this.showUseSystem_(themeId, useSystemTheme); }, /** @private */
diff --git a/chrome/browser/resources/settings/appearance_page/compiled_resources2.gyp b/chrome/browser/resources/settings/appearance_page/compiled_resources2.gyp index 2f218e8..21443a8 100644 --- a/chrome/browser/resources/settings/appearance_page/compiled_resources2.gyp +++ b/chrome/browser/resources/settings/appearance_page/compiled_resources2.gyp
@@ -21,6 +21,7 @@ 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', '<(EXTERNS_GYP):management', '<(EXTERNS_GYP):chrome_send', ],
diff --git a/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.html b/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.html index d4d3f6a4..aea7ed44 100644 --- a/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.html +++ b/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.html
@@ -1,20 +1,16 @@ -<link rel="import" href="chrome://resources/cr_elements/cr_shared_menu/cr_shared_menu.html"> <link rel="import" href="chrome://resources/html/action_link.html"> <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> <link rel="import" href="/passwords_and_forms_page/address_edit_dialog.html"> <link rel="import" href="/passwords_and_forms_page/credit_card_edit_dialog.html"> <link rel="import" href="/passwords_and_forms_page/passwords_shared_css.html"> +<link rel="import" href="/settings_action_menu.html"> <link rel="import" href="/settings_shared_css.html"> <dom-module id="settings-autofill-section"> <link rel="import" type="css" href="chrome://resources/css/action_link.css"> <template> <style include="settings-shared passwords-shared"> - .menu-item { - @apply(--settings-actionable); - } - .type-column { align-items: center; flex: 2; @@ -63,15 +59,16 @@ <a is="action-link" on-tap="onAddAddressTap_">$i18n{addAddress}</a> </div> </div> - <cr-shared-menu id="addressSharedMenu"> - <button id="menuEditAddress" class="list-item menu-item" + <dialog is="settings-action-menu" id="addressSharedMenu"> + <button id="menuEditAddress" class="dropdown-item" on-tap="onMenuEditAddressTap_">$i18n{editAddress}</button> - <button id="menuRemoveAddress" class="list-item menu-item" + <button id="menuRemoveAddress" class="dropdown-item" + hidden$="[[!activeAddress.metadata.isLocal]]" on-tap="onMenuRemoveAddressTap_">$i18n{removeAddress}</button> - </cr-shared-menu> - <template is="dom-if" if="[[activeAddress]]" restamp> + </dialog> + <template is="dom-if" if="[[showAddressDialog_]]" restamp> <settings-address-edit-dialog address="[[activeAddress]]" - on-close="unstampAddressEditDialog_"> + on-close="onAddressDialogClosed_"> </settings-address-edit-dialog> </template> <div class="settings-box first"> @@ -118,17 +115,21 @@ </a> </div> </div> - <cr-shared-menu id="creditCardSharedMenu"> - <button id="menuEditCreditCard" class="list-item menu-item" + <dialog is="settings-action-menu" id="creditCardSharedMenu"> + <button id="menuEditCreditCard" class="dropdown-item" on-tap="onMenuEditCreditCardTap_">$i18n{editCreditCard}</button> - <button id="menuRemoveCreditCard" class="list-item menu-item" + <button id="menuRemoveCreditCard" class="dropdown-item" + hidden$="[[!activeCreditCard.metadata.isLocal]]" on-tap="onMenuRemoveCreditCardTap_">$i18n{removeCreditCard}</button> - <button id="menuClearCreditCard" class="list-item menu-item" - on-tap="onMenuClearCreditCardTap_">$i18n{clearCreditCard}</button> - </cr-shared-menu> - <template is="dom-if" if="[[activeCreditCard]]" restamp> + <button id="menuClearCreditCard" class="dropdown-item" + on-tap="onMenuClearCreditCardTap_" + hidden$="[[!activeCreditCard.metadata.isCached]]"> + $i18n{clearCreditCard} + </button> + </dialog> + <template is="dom-if" if="[[showCreditCardDialog_]]" restamp> <settings-credit-card-edit-dialog credit-card="[[activeCreditCard]]" - on-close="unstampCreditCardEditDialog_"> + on-close="onCreditCardDialogClosed_"> </settings-credit-card-edit-dialog> </template> </template>
diff --git a/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.js b/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.js index 9fa0edf..e92067ed 100644 --- a/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.js +++ b/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.js
@@ -22,11 +22,14 @@ addresses: Array, /** - * Assigning a non-null value triggers the add/edit dialog. + * The model for any address related action menus or dialogs. * @private {?chrome.autofillPrivate.AddressEntry} */ activeAddress: Object, + /** @private */ + showAddressDialog_: Boolean, + /** * An array of saved addresses. * @type {!Array<!chrome.autofillPrivate.CreditCardEntry>} @@ -34,15 +37,13 @@ creditCards: Array, /** - * Assigning a non-null value triggers the add/edit dialog. + * The model for any credit card related action menus or dialogs. * @private {?chrome.autofillPrivate.CreditCardEntry} */ activeCreditCard: Object, - }, - listeners: { - 'addressList.scroll': 'closeMenu_', - 'creditCardList.scroll': 'closeMenu_', + /** @private */ + showCreditCardDialog_: Boolean, }, /** @@ -64,30 +65,32 @@ }, /** - * Toggles the address overflow menu. + * Open the address action menu. * @param {!Event} e The polymer event. * @private */ onAddressMenuTap_: function(e) { - // Close the other menu. - this.$.creditCardSharedMenu.closeMenu(); - var menuEvent = /** @type {!{model: !{item: !Object}}} */(e); - var address = /** @type {!chrome.autofillPrivate.AddressEntry} */( + this.activeAddress = /** @type {!chrome.autofillPrivate.AddressEntry} */( menuEvent.model.item); - this.$.menuRemoveAddress.hidden = !address.metadata.isLocal; - this.$.addressSharedMenu.toggleMenu(Polymer.dom(e).localTarget, address); - e.stopPropagation(); // Prevent the tap event from closing the menu. + + var dotsButton = /** @type {!HTMLElement} */ (Polymer.dom(e).localTarget); + /** @type {!SettingsActionMenuElement} */ ( + this.$.addressSharedMenu).showAt(dotsButton); }, /** * Handles tapping on the "Add address" button. - * @param {!Event} e * @private */ - onAddAddressTap_: function(e) { - e.preventDefault(); + onAddAddressTap_: function() { this.activeAddress = {}; + this.showAddressDialog_ = true; + }, + + /** @private */ + onAddressDialogClosed_: function() { + this.showAddressDialog_ = false; }, /** @@ -95,21 +98,12 @@ * @private */ onMenuEditAddressTap_: function() { - var menu = this.$.addressSharedMenu; - /** @type {chrome.autofillPrivate.AddressEntry} */ - var address = menu.itemData; - - if (address.metadata.isLocal) - this.activeAddress = address; + if (this.activeAddress.metadata.isLocal) + this.showAddressDialog_ = true; else window.open(this.i18n('manageAddressesUrl')); - menu.closeMenu(); - }, - - /** @private */ - unstampAddressEditDialog_: function(e) { - this.activeAddress = null; + this.$.addressSharedMenu.close(); }, /** @@ -117,28 +111,24 @@ * @private */ onMenuRemoveAddressTap_: function() { - var menu = this.$.addressSharedMenu; - this.fire('remove-address', menu.itemData); - menu.closeMenu(); + this.fire('remove-address', this.activeAddress); + this.$.addressSharedMenu.close(); }, /** - * Toggles the credit card overflow menu. + * Opens the credit card action menu. * @param {!Event} e The polymer event. * @private */ onCreditCardMenuTap_: function(e) { - // Close the other menu. - this.$.addressSharedMenu.closeMenu(); - var menuEvent = /** @type {!{model: !{item: !Object}}} */(e); - var creditCard = /** @type {!chrome.autofillPrivate.CreditCardEntry} */( - menuEvent.model.item); - this.$.menuRemoveCreditCard.hidden = !creditCard.metadata.isLocal; - this.$.menuClearCreditCard.hidden = !creditCard.metadata.isCached; - this.$.creditCardSharedMenu.toggleMenu( - Polymer.dom(e).localTarget, creditCard); - e.stopPropagation(); // Prevent the tap event from closing the menu. + this.activeCreditCard = + /** @type {!chrome.autofillPrivate.CreditCardEntry} */( + menuEvent.model.item); + + var dotsButton = /** @type {!HTMLElement} */ (Polymer.dom(e).localTarget); + /** @type {!SettingsActionMenuElement} */ ( + this.$.creditCardSharedMenu).showAt(dotsButton); }, /** @@ -153,7 +143,12 @@ expirationMonth: expirationMonth.toString(), expirationYear: date.getFullYear().toString(), }; - e.preventDefault(); + this.showCreditCardDialog_ = true; + }, + + /** @private */ + onCreditCardDialogClosed_: function() { + this.showCreditCardDialog_ = false; }, /** @@ -161,31 +156,22 @@ * @private */ onMenuEditCreditCardTap_: function() { - var menu = this.$.creditCardSharedMenu; - /** @type {chrome.autofillPrivate.CreditCardEntry} */ - var creditCard = menu.itemData; - - if (creditCard.metadata.isLocal) - this.activeCreditCard = creditCard; + if (this.activeCreditCard.metadata.isLocal) + this.showCreditCardDialog_ = true; else window.open(this.i18n('manageCreditCardsUrl')); - menu.closeMenu(); + this.$.creditCardSharedMenu.close(); }, - /** @private */ - unstampCreditCardEditDialog_: function(e) { - this.activeCreditCard = null; - }, /** * Handles tapping on the "Remove" credit card button. * @private */ onMenuRemoveCreditCardTap_: function() { - var menu = this.$.creditCardSharedMenu; - this.fire('remove-credit-card', menu.itemData); - menu.closeMenu(); + this.fire('remove-credit-card', this.activeCreditCard); + this.$.creditCardSharedMenu.close(); }, /** @@ -193,18 +179,8 @@ * @private */ onMenuClearCreditCardTap_: function() { - var menu = this.$.creditCardSharedMenu; - this.fire('clear-credit-card', menu.itemData); - menu.closeMenu(); - }, - - /** - * Closes the overflow menus. - * @private - */ - closeMenu_: function() { - this.$.addressSharedMenu.closeMenu(); - this.$.creditCardSharedMenu.closeMenu(); + this.fire('clear-credit-card', this.activeCreditCard); + this.$.creditCardSharedMenu.close(); }, /**
diff --git a/chrome/browser/resources/settings/passwords_and_forms_page/compiled_resources2.gyp b/chrome/browser/resources/settings/passwords_and_forms_page/compiled_resources2.gyp index 26eeb69..287be01b 100644 --- a/chrome/browser/resources/settings/passwords_and_forms_page/compiled_resources2.gyp +++ b/chrome/browser/resources/settings/passwords_and_forms_page/compiled_resources2.gyp
@@ -21,7 +21,7 @@ { 'target_name': 'autofill_section', 'dependencies': [ - '<(DEPTH)/ui/webui/resources/cr_elements/cr_shared_menu/compiled_resources2.gyp:cr_shared_menu', + '../compiled_resources2.gyp:settings_action_menu', '<(EXTERNS_GYP):autofill_private', 'address_edit_dialog', 'credit_card_edit_dialog',
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html index 0582e64e..822fca2 100644 --- a/chrome/browser/resources/settings/privacy_page/privacy_page.html +++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -144,7 +144,7 @@ class="settings-box two-line" actionable on-tap="onSiteSettingsTap_"> <div class="start"> - <div>$i18n{siteSettings}</div> + <div>[[siteSettingsPageTitle_()]]</div> <div class="secondary">$i18n{siteSettingsDescription}</div> </div> <button class="subpage-arrow" is="paper-icon-button-light"></button> @@ -172,7 +172,7 @@ <settings-subpage associated-control="[[$$('#site-settings-subpage-trigger')]]" id="site-settings" - page-title="$i18n{siteSettings}"> + page-title="[[siteSettingsPageTitle_()]]"> <settings-site-settings-page category-selected="{{categorySelected}}"> </settings-site-settings-page> </settings-subpage>
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.js b/chrome/browser/resources/settings/privacy_page/privacy_page.js index f02f8e1..50b48d9c 100644 --- a/chrome/browser/resources/settings/privacy_page/privacy_page.js +++ b/chrome/browser/resources/settings/privacy_page/privacy_page.js
@@ -125,4 +125,15 @@ this.$.spellingServiceToggleButton.checked); }, </if> + + /** + * The sub-page title for the site or content settings. + * @return {string} + * @private + */ + siteSettingsPageTitle_: function() { + return loadTimeData.getBoolean('enableSiteSettings') ? + loadTimeData.getString('siteSettings') : + loadTimeData.getString('contentSettings'); + }, });
diff --git a/chrome/browser/resources/settings/route.js b/chrome/browser/resources/settings/route.js index 91de6571..03a4613e 100644 --- a/chrome/browser/resources/settings/route.js +++ b/chrome/browser/resources/settings/route.js
@@ -227,8 +227,14 @@ assert(routeObservers_.delete(this)); }, - /** @abstract */ - currentRouteChanged: assertNotReached, + /** + * @param {!settings.Route|undefined} opt_newRoute + * @param {!settings.Route|undefined} opt_oldRoute + * @abstract + */ + currentRouteChanged: function(opt_newRoute, opt_oldRoute) { + assertNotReached(); + }, }; /**
diff --git a/chrome/browser/resources/settings/site_settings/site_list.html b/chrome/browser/resources/settings/site_settings/site_list.html index 6d6a825..f7e3a533 100644 --- a/chrome/browser/resources/settings/site_settings/site_list.html +++ b/chrome/browser/resources/settings/site_settings/site_list.html
@@ -29,9 +29,7 @@ <paper-submenu id="category" hidden on-paper-submenu-open="onToggle_" on-paper-submenu-close="onToggle_"> <div class="menu-trigger settings-box" hidden$="[[allSites]]" actionable> - <div class="flex" id="header"> - [[computeSiteListHeader_(sites, categoryEnabled)]] - </div> + <div class="flex" id="header">[[categoryHeader]]</div> <iron-icon id="icon" icon="cr:expand-more"></iron-icon> </div>
diff --git a/chrome/browser/resources/settings/site_settings/site_list.js b/chrome/browser/resources/settings/site_settings/site_list.js index 41f844e..8c6af1e 100644 --- a/chrome/browser/resources/settings/site_settings/site_list.js +++ b/chrome/browser/resources/settings/site_settings/site_list.js
@@ -474,29 +474,6 @@ }, /** - * Returns the appropriate header value for display. - * @param {Array<string>} siteList The list of all sites to display for this - * category subtype. - * @param {boolean} toggleState The state of the global toggle for this - * category. - * @private - */ - computeSiteListHeader_: function(siteList, toggleState) { - var title = ''; - if (this.categorySubtype == settings.PermissionValues.ALLOW) { - title = loadTimeData.getString( - toggleState ? 'siteSettingsAllow' : 'siteSettingsExceptions'); - } else if (this.categorySubtype == settings.PermissionValues.BLOCK) { - title = loadTimeData.getString('siteSettingsBlock'); - } else if (this.categorySubtype == settings.PermissionValues.SESSION_ONLY) { - title = loadTimeData.getString('siteSettingsSessionOnly'); - } else { - return title; - } - return loadTimeData.getStringF('titleAndCount', title, siteList.length); - }, - - /** * Returns the appropriate site description to display. This can, for example, * be blank, an 'embedded on <site>' or 'Current incognito session' (or a * mix of the last two).
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_category.html b/chrome/browser/resources/settings/site_settings/site_settings_category.html index d60c271..3bd99ed6 100644 --- a/chrome/browser/resources/settings/site_settings/site_settings_category.html +++ b/chrome/browser/resources/settings/site_settings/site_settings_category.html
@@ -85,18 +85,21 @@ category="[[category]]" category-subtype="[[PermissionValues.BLOCK]]" category-enabled="[[categoryEnabled]]" - selected-site="{{selectedSite}}"> - </site-list> - <site-list - category="[[category]]" - category-subtype="[[PermissionValues.ALLOW]]" - category-enabled="[[categoryEnabled]]" + category-header="$i18n{siteSettingsBlock}" selected-site="{{selectedSite}}"> </site-list> <site-list category="[[category]]" category-subtype="[[PermissionValues.SESSION_ONLY]]" category-enabled="[[categoryEnabled]]" + category-header="$i18n{siteSettingsSessionOnly}" + selected-site="{{selectedSite}}"> + </site-list> + <site-list + category="[[category]]" + category-subtype="[[PermissionValues.ALLOW]]" + category-enabled="[[categoryEnabled]]" + category-header="$i18n{siteSettingsAllow}" selected-site="{{selectedSite}}"> </site-list>
diff --git a/chrome/browser/ui/browser_dialogs.h b/chrome/browser/ui/browser_dialogs.h index ee266a5..0bc1b7e 100644 --- a/chrome/browser/ui/browser_dialogs.h +++ b/chrome/browser/ui/browser_dialogs.h
@@ -174,12 +174,19 @@ #if defined(OS_CHROMEOS) +// This callback informs the package name of the app selected by the user, along +// with the reason why the Bubble was closed. The string param must have a valid +// package name, except when the CloseReason is ERROR or DIALOG_DEACTIVATED, for +// these cases we return a dummy value which won't be used at all and has no +// significance. +using IntentPickerResponse = + base::Callback<void(std::string, arc::ArcNavigationThrottle::CloseReason)>; + // Return a pointer to the IntentPickerBubbleView::ShowBubble method. -using BubbleShowPtr = void (*)( - content::WebContents*, - const std::vector<std::pair<std::basic_string<char>, gfx::Image>>&, - const base::Callback<void(size_t, - arc::ArcNavigationThrottle::CloseReason)>&); +using BubbleShowPtr = + void (*)(content::WebContents*, + const std::vector<arc::ArcNavigationThrottle::AppInfo>&, + const IntentPickerResponse&); BubbleShowPtr ShowIntentPickerBubble();
diff --git a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm index 6f036c27..e9ee2d4e 100644 --- a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm +++ b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm
@@ -688,6 +688,7 @@ GetToolbarModel()->GetSecurityLevel(false); bool has_verbose_for_security = security == security_state::SecurityStateModel::DANGEROUS || + security == security_state::SecurityStateModel::HTTP_SHOW_WARNING || (IsSecureConnection(security) && should_show_secure_verbose_); return has_verbose_for_security && !omnibox_view_->IsEditingOrEmpty() &&
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view.cc b/chrome/browser/ui/views/intent_picker_bubble_view.cc index f805497..18f8127a 100644 --- a/chrome/browser/ui/views/intent_picker_bubble_view.cc +++ b/chrome/browser/ui/views/intent_picker_bubble_view.cc
@@ -15,7 +15,9 @@ #include "content/public/browser/navigation_handle.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/gfx/canvas.h" +#include "ui/views/animation/ink_drop_host_view.h" #include "ui/views/border.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/scroll_view.h" @@ -29,46 +31,81 @@ // Using |kMaxAppResults| as a measure of how many apps we want to show. constexpr size_t kMaxAppResults = arc::ArcNavigationThrottle::kMaxAppResults; // Main components sizes +constexpr int kDialogDelegateInsets = 16; constexpr int kRowHeight = 40; constexpr int kMaxWidth = 320; -constexpr int kHeaderHeight = 60; -constexpr int kFooterHeight = 68; -// Inter components padding -constexpr int kButtonSeparation = 8; -constexpr int kLabelImageSeparation = 12; // UI position wrt the Top Container constexpr int kTopContainerMerge = 3; -constexpr size_t kAppTagNoneSelected = static_cast<size_t>(-1); -// Arbitrary negative values to use as tags on the |always_button_| and -// |just_once_button_|. These are negative to differentiate from the app's tags -// which are always >= 0. -enum class Option : int { ALWAYS = -2, JUST_ONCE }; +constexpr char kInvalidPackageName[] = ""; } // namespace +// IntentPickerLabelButton + +// A button that represents a candidate intent handler. +class IntentPickerLabelButton : public views::LabelButton { + public: + IntentPickerLabelButton(views::ButtonListener* listener, + gfx::Image* icon, + const std::string& package_name, + const std::string& activity_name) + : LabelButton(listener, + base::UTF8ToUTF16(base::StringPiece(activity_name))), + package_name_(package_name) { + SetHorizontalAlignment(gfx::ALIGN_LEFT); + SetFocusBehavior(View::FocusBehavior::ALWAYS); + SetMinSize(gfx::Size(kMaxWidth, kRowHeight)); + SetInkDropMode(InkDropMode::ON); + if (!icon->IsEmpty()) + SetImage(views::ImageButton::STATE_NORMAL, *icon->ToImageSkia()); + SetBorder(views::Border::CreateEmptyBorder(10, 16, 10, 0)); + } + + SkColor GetInkDropBaseColor() const override { return SK_ColorBLACK; } + + void MarkAsUnselected(const ui::Event* event) { + AnimateInkDrop(views::InkDropState::DEACTIVATED, + ui::LocatedEvent::FromIfValid(event)); + } + + void MarkAsSelected(const ui::Event* event) { + AnimateInkDrop(views::InkDropState::ACTIVATED, + ui::LocatedEvent::FromIfValid(event)); + } + + views::InkDropState GetTargetInkDropState() { + return ink_drop()->GetTargetInkDropState(); + } + + private: + std::string package_name_; + + DISALLOW_COPY_AND_ASSIGN(IntentPickerLabelButton); +}; + // static void IntentPickerBubbleView::ShowBubble( content::WebContents* web_contents, - const std::vector<NameAndIcon>& app_info, - const ThrottleCallback& throttle_cb) { + const std::vector<AppInfo>& app_info, + const IntentPickerResponse& intent_picker_cb) { Browser* browser = chrome::FindBrowserWithWebContents(web_contents); - if (!browser) { - throttle_cb.Run(kAppTagNoneSelected, - arc::ArcNavigationThrottle::CloseReason::ERROR); + if (!browser || !BrowserView::GetBrowserViewForBrowser(browser)) { + intent_picker_cb.Run(kInvalidPackageName, + arc::ArcNavigationThrottle::CloseReason::ERROR); return; } BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); - if (!browser_view) { - throttle_cb.Run(kAppTagNoneSelected, - arc::ArcNavigationThrottle::CloseReason::ERROR); - return; - } - IntentPickerBubbleView* delegate = - new IntentPickerBubbleView(app_info, throttle_cb, web_contents); - delegate->set_margins(gfx::Insets()); + new IntentPickerBubbleView(app_info, intent_picker_cb, web_contents); + // TODO(djacobo): Remove the left and right insets when + // http://crbug.com/656662 gets fixed. + // Add a 1-pixel extra boundary left and right when using secondary UI. + if (ui::MaterialDesignController::IsSecondaryUiMaterial()) + delegate->set_margins(gfx::Insets(16, 1, 0, 1)); + else + delegate->set_margins(gfx::Insets(16, 0, 0, 0)); delegate->set_parent_window(browser_view->GetNativeWindow()); views::Widget* widget = views::BubbleDialogDelegateView::CreateBubble(delegate); @@ -85,104 +122,65 @@ browser_view->GetTopContainerBoundsInScreen().width(), browser_view->GetTopContainerBoundsInScreen().height() - kTopContainerMerge)); + delegate->GetDialogClientView()->set_button_row_insets( + gfx::Insets(kDialogDelegateInsets)); + delegate->GetDialogClientView()->Layout(); + delegate->GetIntentPickerLabelButtonAt(0)->MarkAsSelected(nullptr); widget->Show(); } // static std::unique_ptr<IntentPickerBubbleView> IntentPickerBubbleView::CreateBubbleView( - const std::vector<NameAndIcon>& app_info, - const ThrottleCallback& throttle_cb, + const std::vector<AppInfo>& app_info, + const IntentPickerResponse& intent_picker_cb, content::WebContents* web_contents) { std::unique_ptr<IntentPickerBubbleView> bubble( - new IntentPickerBubbleView(app_info, throttle_cb, web_contents)); + new IntentPickerBubbleView(app_info, intent_picker_cb, web_contents)); bubble->Init(); return bubble; } -void IntentPickerBubbleView::Init() { - SkColor button_text_color = SkColorSetRGB(0x42, 0x85, 0xf4); +bool IntentPickerBubbleView::Accept() { + RunCallback(app_info_[selected_app_tag_].package_name, + arc::ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED); + return true; +} +bool IntentPickerBubbleView::Cancel() { + RunCallback(app_info_[selected_app_tag_].package_name, + arc::ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED); + return true; +} + +bool IntentPickerBubbleView::Close() { + // Whenever closing the bubble without pressing |Just once| or |Always| we + // need to report back that the user didn't select anything. + RunCallback(kInvalidPackageName, + arc::ArcNavigationThrottle::CloseReason::DIALOG_DEACTIVATED); + return true; +} + +void IntentPickerBubbleView::Init() { views::BoxLayout* general_layout = new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0); SetLayoutManager(general_layout); - // Create a view for the upper part of the UI, managed by a GridLayout to - // allow precise padding. - View* header = new View(); - views::GridLayout* header_layout = new views::GridLayout(header); - header->SetLayoutManager(header_layout); - views::Label* open_with = new views::Label( - l10n_util::GetStringUTF16(IDS_INTENT_PICKER_BUBBLE_VIEW_OPEN_WITH)); - open_with->SetHorizontalAlignment(gfx::ALIGN_LEFT); - open_with->SetFontList(gfx::FontList("Roboto-medium, 15px")); - - views::ColumnSet* column_set = header_layout->AddColumnSet(0); - column_set->AddPaddingColumn(0, 16); - column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, - views::GridLayout::FIXED, kMaxWidth, kMaxWidth); - column_set->AddPaddingColumn(0, 16); - // Tell the GridLayout where to start, then proceed to place the views. - header_layout->AddPaddingRow(0.0, 21); - header_layout->StartRow(0, 0); - header_layout->AddView(open_with); - header_layout->AddPaddingRow(0.0, 24); - - always_button_ = new views::LabelButton( - this, l10n_util::GetStringUTF16(IDS_INTENT_PICKER_BUBBLE_VIEW_ALWAYS)); - always_button_->SetFocusBehavior(View::FocusBehavior::ALWAYS); - always_button_->SetFontList(gfx::FontList("Roboto-medium, 13px")); - always_button_->set_tag(static_cast<int>(Option::ALWAYS)); - always_button_->SetMinSize(gfx::Size(80, 32)); - always_button_->SetTextColor(views::Button::STATE_DISABLED, SK_ColorGRAY); - always_button_->SetTextColor(views::Button::STATE_NORMAL, button_text_color); - always_button_->SetTextColor(views::Button::STATE_HOVERED, button_text_color); - always_button_->SetHorizontalAlignment(gfx::ALIGN_CENTER); - always_button_->SetState(views::Button::STATE_DISABLED); - always_button_->SetBorder(views::Border::CreateEmptyBorder(gfx::Insets(16))); - - just_once_button_ = new views::LabelButton( - this, l10n_util::GetStringUTF16(IDS_INTENT_PICKER_BUBBLE_VIEW_JUST_ONCE)); - just_once_button_->SetFocusBehavior(View::FocusBehavior::ALWAYS); - just_once_button_->SetFontList(gfx::FontList("Roboto-medium, 13px")); - just_once_button_->set_tag(static_cast<int>(Option::JUST_ONCE)); - just_once_button_->SetMinSize(gfx::Size(80, 32)); - just_once_button_->SetState(views::Button::STATE_DISABLED); - just_once_button_->SetTextColor(views::Button::STATE_DISABLED, SK_ColorGRAY); - just_once_button_->SetTextColor(views::Button::STATE_NORMAL, - button_text_color); - just_once_button_->SetTextColor(views::Button::STATE_HOVERED, - button_text_color); - just_once_button_->SetHorizontalAlignment(gfx::ALIGN_CENTER); - just_once_button_->SetBorder( - views::Border::CreateEmptyBorder(gfx::Insets(16))); - - header_layout->StartRow(0, 0); - AddChildView(header); - // Creates a view to hold the views for each app. views::View* scrollable_view = new views::View(); views::BoxLayout* scrollable_layout = new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0); scrollable_view->SetLayoutManager(scrollable_layout); for (size_t i = 0; i < app_info_.size(); ++i) { - views::LabelButton* tmp_label = new views::LabelButton( - this, base::UTF8ToUTF16(base::StringPiece(app_info_[i].first))); - tmp_label->SetFocusBehavior(View::FocusBehavior::ALWAYS); - tmp_label->SetFontList(gfx::FontList("Roboto-regular, 13px")); - tmp_label->SetImageLabelSpacing(kLabelImageSeparation); - tmp_label->SetMinSize(gfx::Size(kMaxWidth, kRowHeight)); - tmp_label->SetMaxSize(gfx::Size(kMaxWidth, kRowHeight)); - tmp_label->set_tag(i); - if (!app_info_[i].second.IsEmpty()) { - tmp_label->SetImage(views::ImageButton::STATE_NORMAL, - *app_info_[i].second.ToImageSkia()); - } - tmp_label->SetBorder(views::Border::CreateEmptyBorder(10, 16, 10, 0)); - scrollable_view->AddChildViewAt(tmp_label, i); + IntentPickerLabelButton* app_button = new IntentPickerLabelButton( + this, &app_info_[i].icon, app_info_[i].package_name, + app_info_[i].activity_name); + app_button->set_tag(i); + scrollable_view->AddChildViewAt(app_button, i); } scroll_view_ = new views::ScrollView(); + scroll_view_->EnableViewPortLayer(); scroll_view_->SetContents(scrollable_view); // Setting a customized ScrollBar which is shown only when the mouse pointer // is inside the ScrollView. @@ -197,34 +195,28 @@ scroll_view_->ClipHeightTo(kRowHeight, (kMaxAppResults + 0.5) * kRowHeight); } AddChildView(scroll_view_); +} - // The lower part of the Picker contains only the 2 buttons - // |just_once_button_| and |always_button_|. - View* footer = new View(); - footer->SetBorder(views::Border::CreateEmptyBorder(24, 0, 12, 12)); - views::BoxLayout* buttons_layout = new views::BoxLayout( - views::BoxLayout::kHorizontal, 0, 0, kButtonSeparation); - footer->SetLayoutManager(buttons_layout); +base::string16 IntentPickerBubbleView::GetWindowTitle() const { + return l10n_util::GetStringUTF16(IDS_INTENT_PICKER_BUBBLE_VIEW_OPEN_WITH); +} - buttons_layout->set_main_axis_alignment( - views::BoxLayout::MAIN_AXIS_ALIGNMENT_END); - footer->AddChildView(just_once_button_); - footer->AddChildView(always_button_); - AddChildView(footer); +base::string16 IntentPickerBubbleView::GetDialogButtonLabel( + ui::DialogButton button) const { + return l10n_util::GetStringUTF16(button == ui::DIALOG_BUTTON_OK + ? IDS_INTENT_PICKER_BUBBLE_VIEW_JUST_ONCE + : IDS_INTENT_PICKER_BUBBLE_VIEW_ALWAYS); } IntentPickerBubbleView::IntentPickerBubbleView( - const std::vector<NameAndIcon>& app_info, - ThrottleCallback throttle_cb, + const std::vector<AppInfo>& app_info, + IntentPickerResponse intent_picker_cb, content::WebContents* web_contents) : views::BubbleDialogDelegateView(nullptr /* anchor_view */, views::BubbleBorder::TOP_CENTER), WebContentsObserver(web_contents), - was_callback_run_(false), - throttle_cb_(throttle_cb), - selected_app_tag_(kAppTagNoneSelected), - always_button_(nullptr), - just_once_button_(nullptr), + intent_picker_cb_(intent_picker_cb), + selected_app_tag_(0), scroll_view_(nullptr), app_info_(app_info) {} @@ -235,47 +227,18 @@ // If the widget gets closed without an app being selected we still need to use // the callback so the caller can Resume the navigation. void IntentPickerBubbleView::OnWidgetDestroying(views::Widget* widget) { - if (!was_callback_run_) { - was_callback_run_ = true; - throttle_cb_.Run( - kAppTagNoneSelected, - arc::ArcNavigationThrottle::CloseReason::DIALOG_DEACTIVATED); - } -} - -int IntentPickerBubbleView::GetDialogButtons() const { - return ui::DIALOG_BUTTON_NONE; + RunCallback(kInvalidPackageName, + arc::ArcNavigationThrottle::CloseReason::DIALOG_DEACTIVATED); } void IntentPickerBubbleView::ButtonPressed(views::Button* sender, const ui::Event& event) { - switch (sender->tag()) { - case static_cast<int>(Option::ALWAYS): - was_callback_run_ = true; - throttle_cb_.Run(selected_app_tag_, - arc::ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED); - GetWidget()->Close(); - break; - case static_cast<int>(Option::JUST_ONCE): - was_callback_run_ = true; - throttle_cb_.Run( - selected_app_tag_, - arc::ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED); - GetWidget()->Close(); - break; - default: - // The selected app must be a value in the range [0, app_info_.size()-1]. - DCHECK(static_cast<size_t>(sender->tag()) < app_info_.size()); - // The user cannot click on the |always_button_| or |just_once_button_| - // unless an explicit app has been selected. - if (selected_app_tag_ != kAppTagNoneSelected) - SetLabelButtonBackgroundColor(selected_app_tag_, SK_ColorWHITE); - selected_app_tag_ = sender->tag(); - SetLabelButtonBackgroundColor(selected_app_tag_, - SkColorSetRGB(0xeb, 0xeb, 0xeb)); - always_button_->SetState(views::Button::STATE_NORMAL); - just_once_button_->SetState(views::Button::STATE_NORMAL); - } + // The selected app must be a value in the range [0, app_info_.size()-1]. + DCHECK_LT(static_cast<size_t>(sender->tag()), app_info_.size()); + GetIntentPickerLabelButtonAt(selected_app_tag_)->MarkAsUnselected(&event); + + selected_app_tag_ = sender->tag(); + GetIntentPickerLabelButtonAt(selected_app_tag_)->MarkAsSelected(&event); } gfx::Size IntentPickerBubbleView::GetPreferredSize() const { @@ -289,29 +252,48 @@ } else { apps_height *= kRowHeight; } - ps.set_height(apps_height + kHeaderHeight + kFooterHeight); + ps.set_height(apps_height + kDialogDelegateInsets); return ps; } // If the actual web_contents gets destroyed in the middle of the process we // should inform the caller about this error. void IntentPickerBubbleView::WebContentsDestroyed() { - if (!was_callback_run_) { - was_callback_run_ = true; - throttle_cb_.Run(kAppTagNoneSelected, - arc::ArcNavigationThrottle::CloseReason::ERROR); - } GetWidget()->Close(); } -views::LabelButton* IntentPickerBubbleView::GetLabelButtonAt(size_t index) { +IntentPickerLabelButton* IntentPickerBubbleView::GetIntentPickerLabelButtonAt( + size_t index) { views::View* temp_contents = scroll_view_->contents(); - return static_cast<views::LabelButton*>(temp_contents->child_at(index)); + return static_cast<IntentPickerLabelButton*>(temp_contents->child_at(index)); } -void IntentPickerBubbleView::SetLabelButtonBackgroundColor(size_t index, - SkColor color) { - views::LabelButton* temp_lb = GetLabelButtonAt(index); - temp_lb->set_background(views::Background::CreateSolidBackground(color)); - temp_lb->SchedulePaint(); +void IntentPickerBubbleView::RunCallback( + std::string package, + arc::ArcNavigationThrottle::CloseReason close_reason) { + if (!intent_picker_cb_.is_null()) { + // We must ensure |intent_picker_cb_| is only Run() once, this is why we + // have a temporary |callback| helper, so we can set the original callback + // to null and still report back to whoever started the UI. + auto callback = intent_picker_cb_; + intent_picker_cb_.Reset(); + callback.Run(package, close_reason); + } +} + +gfx::ImageSkia IntentPickerBubbleView::GetAppImageForTesting(size_t index) { + return GetIntentPickerLabelButtonAt(index)->GetImage( + views::Button::ButtonState::STATE_NORMAL); +} + +views::InkDropState IntentPickerBubbleView::GetInkDropStateForTesting( + size_t index) { + return GetIntentPickerLabelButtonAt(index)->GetTargetInkDropState(); +} + +void IntentPickerBubbleView::PressButtonForTesting(size_t index, + const ui::Event& event) { + views::Button* button = + static_cast<views::Button*>(GetIntentPickerLabelButtonAt(index)); + ButtonPressed(button, event); }
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view.h b/chrome/browser/ui/views/intent_picker_bubble_view.h index e811528..019deb9 100644 --- a/chrome/browser/ui/views/intent_picker_bubble_view.h +++ b/chrome/browser/ui/views/intent_picker_bubble_view.h
@@ -6,11 +6,13 @@ #define CHROME_BROWSER_UI_VIEWS_INTENT_PICKER_BUBBLE_VIEW_H_ #include <memory> +#include <string> #include <vector> #include "base/callback.h" #include "base/macros.h" #include "chrome/browser/chromeos/arc/arc_navigation_throttle.h" +#include "chrome/browser/ui/browser_dialogs.h" #include "content/public/browser/web_contents_observer.h" #include "ui/gfx/image/image.h" #include "ui/views/bubble/bubble_dialog_delegate.h" @@ -31,6 +33,8 @@ class Event; } // namespace ui +class IntentPickerLabelButton; + // A bubble that displays a list of aplications (icons and names), after the // list we show a pair of buttons which allow the user to remember the selection // or not. This class comunicates the user's selection with a callback used by @@ -43,47 +47,49 @@ // | ... | // | Icon(N) Name(N) | // | | -// | [JUST ONCE] [ALWAYS] | +// | [Just once] [Always] | // +--------------------------------+ class IntentPickerBubbleView : public views::BubbleDialogDelegateView, public views::ButtonListener, public content::WebContentsObserver { public: - using NameAndIcon = arc::ArcNavigationThrottle::NameAndIcon; - // This callback informs the index of the app selected by the user, along with - // the reason why the Bubble was closed. The size_t param must have a value in - // the range [0, app_info.size()-1], except when the CloseReason is ERROR or - // DIALOG_DEACTIVATED, for these cases we return a dummy value - // |kAppTagNoneSelected| which won't be used at all and has no significance. - using ThrottleCallback = - base::Callback<void(size_t, arc::ArcNavigationThrottle::CloseReason)>; + using AppInfo = arc::ArcNavigationThrottle::AppInfo; ~IntentPickerBubbleView() override; static void ShowBubble(content::WebContents* web_contents, - const std::vector<NameAndIcon>& app_info, - const ThrottleCallback& throttle_cb); + const std::vector<AppInfo>& app_info, + const IntentPickerResponse& intent_picker_cb); static std::unique_ptr<IntentPickerBubbleView> CreateBubbleView( - const std::vector<NameAndIcon>& app_info, - const ThrottleCallback& throttle_cb, + const std::vector<AppInfo>& app_info, + const IntentPickerResponse& intent_picker_cb, content::WebContents* web_contents); + // views::BubbleDialogDelegateView overrides: + bool Accept() override; + bool Cancel() override; + bool Close() override; + protected: // views::BubbleDialogDelegateView overrides: void Init() override; + base::string16 GetWindowTitle() const override; + base::string16 GetDialogButtonLabel(ui::DialogButton button) const override; private: friend class IntentPickerBubbleViewTest; FRIEND_TEST_ALL_PREFIXES(IntentPickerBubbleViewTest, NullIcons); FRIEND_TEST_ALL_PREFIXES(IntentPickerBubbleViewTest, NonNullIcons); FRIEND_TEST_ALL_PREFIXES(IntentPickerBubbleViewTest, LabelsPtrVectorSize); - IntentPickerBubbleView(const std::vector<NameAndIcon>& app_info, - ThrottleCallback throttle_cb, + FRIEND_TEST_ALL_PREFIXES(IntentPickerBubbleViewTest, VerifyStartingInkDrop); + FRIEND_TEST_ALL_PREFIXES(IntentPickerBubbleViewTest, InkDropStateTransition); + FRIEND_TEST_ALL_PREFIXES(IntentPickerBubbleViewTest, PressButtonTwice); + IntentPickerBubbleView(const std::vector<AppInfo>& app_info, + IntentPickerResponse intent_picker_cb, content::WebContents* web_contents); // views::BubbleDialogDelegateView overrides: void OnWidgetDestroying(views::Widget* widget) override; - int GetDialogButtons() const override; // views::ButtonListener overrides: void ButtonPressed(views::Button* sender, const ui::Event& event) override; @@ -94,26 +100,25 @@ // content::WebContentsObserver overrides: void WebContentsDestroyed() override; - // Retrieves the LabelButton* contained at position |index| from the internal - // ScrollView. - views::LabelButton* GetLabelButtonAt(size_t index); - void SetLabelButtonBackgroundColor(size_t index, SkColor color); + // Retrieves the IntentPickerLabelButton* contained at position |index| from + // the internal ScrollView. + IntentPickerLabelButton* GetIntentPickerLabelButtonAt(size_t index); + void RunCallback(std::string package, + arc::ArcNavigationThrottle::CloseReason close_reason); - // Flag set to true iff the callback was Run at some previous step, used to - // ensure we only use the callback once. - bool was_callback_run_; + gfx::ImageSkia GetAppImageForTesting(size_t index); + views::InkDropState GetInkDropStateForTesting(size_t); + void PressButtonForTesting(size_t index, const ui::Event& event); // Callback used to respond to ArcNavigationThrottle. - ThrottleCallback throttle_cb_; + IntentPickerResponse intent_picker_cb_; - // Keeps a invalid value unless the user explicitly makes a decision. - size_t selected_app_tag_; + // Pre-select the first app on the list. + size_t selected_app_tag_ = 0; - views::LabelButton* always_button_; - views::LabelButton* just_once_button_; views::ScrollView* scroll_view_; - std::vector<NameAndIcon> app_info_; + std::vector<AppInfo> app_info_; DISALLOW_COPY_AND_ASSIGN(IntentPickerBubbleView); };
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view_unittest.cc b/chrome/browser/ui/views/intent_picker_bubble_view_unittest.cc index 6a78a69..39ce248 100644 --- a/chrome/browser/ui/views/intent_picker_bubble_view_unittest.cc +++ b/chrome/browser/ui/views/intent_picker_bubble_view_unittest.cc
@@ -4,6 +4,8 @@ #include "chrome/browser/ui/views/intent_picker_bubble_view.h" +#include <string> + #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback.h" @@ -14,16 +16,21 @@ #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/image/image.h" #include "ui/views/controls/button/button.h" -#include "ui/views/controls/button/label_button.h" #include "ui/views/controls/scroll_view.h" #include "ui/views/resources/grit/views_resources.h" #include "url/gurl.h" -using NameAndIcon = arc::ArcNavigationThrottle::NameAndIcon; +using AppInfo = arc::ArcNavigationThrottle::AppInfo; using content::WebContents; using content::OpenURLParams; using content::Referrer; +class MousePressedEvent : public ui::Event { + public: + MousePressedEvent() : Event(ui::ET_MOUSE_PRESSED, base::TimeTicks(), 0) {} + ~MousePressedEvent() override {} +}; + class IntentPickerBubbleViewTest : public BrowserWithTestWindowTest { public: IntentPickerBubbleViewTest() = default; @@ -38,8 +45,8 @@ protected: void CreateBubbleView(bool use_icons) { // Pushing a couple of fake apps just to check they are created on the UI. - app_info_.emplace_back("dank app 1", gfx::Image()); - app_info_.emplace_back("dank app 2", gfx::Image()); + app_info_.emplace_back(AppInfo(gfx::Image(), "package_1", "dank app 1")); + app_info_.emplace_back(AppInfo(gfx::Image(), "package_2", "dank_app_2")); if (use_icons) FillAppListWithDummyIcons(); @@ -61,15 +68,15 @@ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); gfx::Image dummy_icon = rb.GetImageNamed(IDR_CLOSE); for (auto& app : app_info_) - app.second = dummy_icon; + app.icon = dummy_icon; } // Dummy method to be called upon bubble closing. - void OnBubbleClosed(size_t selected_app_tag, + void OnBubbleClosed(std::string selected_app_package, arc::ArcNavigationThrottle::CloseReason close_reason) {} std::unique_ptr<IntentPickerBubbleView> bubble_; - std::vector<NameAndIcon> app_info_; + std::vector<AppInfo> app_info_; private: DISALLOW_COPY_AND_ASSIGN(IntentPickerBubbleViewTest); @@ -80,9 +87,8 @@ CreateBubbleView(false); size_t size = bubble_->app_info_.size(); for (size_t i = 0; i < size; ++i) { - views::LabelButton* app = bubble_->GetLabelButtonAt(i); - EXPECT_TRUE( - app->GetImage(views::Button::ButtonState::STATE_NORMAL).isNull()) << i; + gfx::ImageSkia image = bubble_->GetAppImageForTesting(i); + EXPECT_TRUE(image.isNull()) << i; } } @@ -91,9 +97,8 @@ CreateBubbleView(true); size_t size = bubble_->app_info_.size(); for (size_t i = 0; i < size; ++i) { - views::LabelButton* app = bubble_->GetLabelButtonAt(i); - EXPECT_FALSE( - app->GetImage(views::Button::ButtonState::STATE_NORMAL).isNull()) << i; + gfx::ImageSkia image = bubble_->GetAppImageForTesting(i); + EXPECT_FALSE(image.isNull()) << i; } } @@ -103,3 +108,40 @@ CreateBubbleView(true); EXPECT_EQ(app_info_.size(), bubble_->app_info_.size()); } + +// Verifies the InkDrop state when creating a new bubble. +TEST_F(IntentPickerBubbleViewTest, VerifyStartingInkDrop) { + CreateBubbleView(true); + size_t size = bubble_->app_info_.size(); + for (size_t i = 0; i < size; ++i) { + EXPECT_EQ(bubble_->GetInkDropStateForTesting(i), + views::InkDropState::HIDDEN); + } +} + +// Press each button at a time and make sure it goes to ACTIVATED state, +// followed by HIDDEN state after selecting other button. +TEST_F(IntentPickerBubbleViewTest, InkDropStateTransition) { + CreateBubbleView(true); + size_t size = bubble_->app_info_.size(); + for (size_t i = 0; i < size; ++i) { + bubble_->PressButtonForTesting((i + 1) % size, MousePressedEvent()); + EXPECT_EQ(bubble_->GetInkDropStateForTesting(i), + views::InkDropState::HIDDEN); + EXPECT_EQ(bubble_->GetInkDropStateForTesting((i + 1) % size), + views::InkDropState::ACTIVATED); + } +} + +// Arbitrary press the first button twice, check that the InkDropState remains +// the same. +TEST_F(IntentPickerBubbleViewTest, PressButtonTwice) { + CreateBubbleView(true); + EXPECT_EQ(bubble_->GetInkDropStateForTesting(0), views::InkDropState::HIDDEN); + bubble_->PressButtonForTesting(0, MousePressedEvent()); + EXPECT_EQ(bubble_->GetInkDropStateForTesting(0), + views::InkDropState::ACTIVATED); + bubble_->PressButtonForTesting(0, MousePressedEvent()); + EXPECT_EQ(bubble_->GetInkDropStateForTesting(0), + views::InkDropState::ACTIVATED); +}
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc index 38c7c2e..dc70d7e 100644 --- a/chrome/browser/ui/views/location_bar/location_bar_view.cc +++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -825,7 +825,8 @@ security_state::SecurityStateModel::SecurityLevel security_level = GetToolbarModel()->GetSecurityLevel(false); SkColor icon_color = - (security_level == security_state::SecurityStateModel::NONE) + (security_level == security_state::SecurityStateModel::NONE || + security_level == security_state::SecurityStateModel::HTTP_SHOW_WARNING) ? color_utils::DeriveDefaultIconColor(GetColor(TEXT)) : GetSecureTextColor(security_level); location_icon_view_->SetImage(gfx::CreateVectorIcon( @@ -1014,22 +1015,23 @@ bool LocationBarView::ShouldShowSecurityChip() const { using SecurityLevel = security_state::SecurityStateModel::SecurityLevel; - SecurityLevel level = GetToolbarModel()->GetSecurityLevel(false); - return ((level == SecurityLevel::SECURE || - level == SecurityLevel::EV_SECURE) && - should_show_secure_state_) || - level == SecurityLevel::DANGEROUS; + const SecurityLevel level = GetToolbarModel()->GetSecurityLevel(false); + if (level == SecurityLevel::SECURE || level == SecurityLevel::EV_SECURE) + return should_show_secure_state_; + return level == SecurityLevel::DANGEROUS || + level == SecurityLevel::HTTP_SHOW_WARNING; } bool LocationBarView::ShouldAnimateSecurityChip() const { using SecurityLevel = security_state::SecurityStateModel::SecurityLevel; SecurityLevel level = GetToolbarModel()->GetSecurityLevel(false); - bool is_secure_level = - level == SecurityLevel::EV_SECURE || level == SecurityLevel::SECURE; - return ShouldShowSecurityChip() && - ((level == SecurityLevel::DANGEROUS && - should_animate_nonsecure_state_) || - (is_secure_level && should_animate_secure_state_)); + if (!ShouldShowSecurityChip()) + return false; + if (level == SecurityLevel::SECURE || level == SecurityLevel::EV_SECURE) + return should_animate_secure_state_; + return should_animate_nonsecure_state_ && + (level == SecurityLevel::DANGEROUS || + level == SecurityLevel::HTTP_SHOW_WARNING); } ////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/webui/conflicts_ui.cc b/chrome/browser/ui/webui/conflicts_ui.cc index d39f3678..7a38864 100644 --- a/chrome/browser/ui/webui/conflicts_ui.cc +++ b/chrome/browser/ui/webui/conflicts_ui.cc
@@ -122,7 +122,10 @@ // easy enough to defend against this. if (!observer_.IsObserving(model)) { observer_.Add(model); - model->ScanNow(); + + // Ask the scan to be performed immediately, and not in background mode. + // This ensures the results are available ASAP for the UI. + model->ScanNow(false); } }
diff --git a/chrome/browser/ui/webui/settings/appearance_handler.cc b/chrome/browser/ui/webui/settings/appearance_handler.cc index eac391fb5..e08eae0 100644 --- a/chrome/browser/ui/webui/settings/appearance_handler.cc +++ b/chrome/browser/ui/webui/settings/appearance_handler.cc
@@ -53,7 +53,10 @@ #if defined(OS_LINUX) && !defined(OS_CHROMEOS) void AppearanceHandler::HandleUseSystemTheme(const base::ListValue* args) { - ThemeServiceFactory::GetForProfile(profile_)->UseSystemTheme(); + if (profile_->IsSupervised()) + NOTREACHED(); + else + ThemeServiceFactory::GetForProfile(profile_)->UseSystemTheme(); } #endif
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 f90b41c..722b5709 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
@@ -253,7 +253,8 @@ } #endif -void AddAppearanceStrings(content::WebUIDataSource* html_source) { +void AddAppearanceStrings(content::WebUIDataSource* html_source, + Profile* profile) { LocalizedString localized_strings[] = { {"appearancePageTitle", IDS_SETTINGS_APPEARANCE}, {"exampleDotCom", IDS_SETTINGS_EXAMPLE_DOT_COM}, @@ -284,6 +285,8 @@ }; AddLocalizedStringsBulk(html_source, localized_strings, arraysize(localized_strings)); + + html_source->AddBoolean("isSupervised", profile->IsSupervised()); } #if defined(OS_CHROMEOS) @@ -1225,6 +1228,7 @@ {"manageCertificates", IDS_SETTINGS_MANAGE_CERTIFICATES}, {"manageCertificatesDescription", IDS_SETTINGS_MANAGE_CERTIFICATES_DESCRIPTION}, + {"contentSettings", IDS_SETTINGS_CONTENT_SETTINGS}, {"siteSettings", IDS_SETTINGS_SITE_SETTINGS}, {"siteSettingsDescription", IDS_SETTINGS_SITE_SETTINGS_DESCRIPTION}, {"clearBrowsingData", IDS_SETTINGS_CLEAR_DATA}, @@ -1694,7 +1698,7 @@ #if defined(OS_CHROMEOS) AddAccountUITweaksStrings(html_source, profile); #endif - AddAppearanceStrings(html_source); + AddAppearanceStrings(html_source, profile); #if defined(OS_CHROMEOS) AddBluetoothStrings(html_source); #endif
diff --git a/chrome/browser/ui/webui/welcome_handler.cc b/chrome/browser/ui/webui/welcome_handler.cc index f23d092a..c6b91f6 100644 --- a/chrome/browser/ui/webui/welcome_handler.cc +++ b/chrome/browser/ui/webui/welcome_handler.cc
@@ -18,7 +18,6 @@ WelcomeHandler::WelcomeHandler(content::WebUI* web_ui) : profile_(Profile::FromWebUI(web_ui)), - browser_(chrome::FindBrowserWithWebContents(web_ui->GetWebContents())), oauth2_token_service_( ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)), result_(WelcomeResult::DEFAULT) { @@ -46,7 +45,7 @@ // them away to the NTP instead. GoToNewTabPage(); } else { - browser_->ShowModalSigninWindow( + GetBrowser()->ShowModalSigninWindow( profiles::BubbleViewMode::BUBBLE_VIEW_MODE_GAIA_SIGNIN, signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE); } @@ -69,7 +68,16 @@ } void WelcomeHandler::GoToNewTabPage() { - chrome::NavigateParams params(browser_, GURL(chrome::kChromeUINewTabURL), + chrome::NavigateParams params(GetBrowser(), GURL(chrome::kChromeUINewTabURL), ui::PageTransition::PAGE_TRANSITION_LINK); chrome::Navigate(¶ms); } + +Browser* WelcomeHandler::GetBrowser() { + DCHECK(web_ui()); + content::WebContents* contents = web_ui()->GetWebContents(); + DCHECK(contents); + Browser* browser = chrome::FindBrowserWithWebContents(contents); + DCHECK(browser); + return browser; +}
diff --git a/chrome/browser/ui/webui/welcome_handler.h b/chrome/browser/ui/webui/welcome_handler.h index a9a7af7a..55f1861 100644 --- a/chrome/browser/ui/webui/welcome_handler.h +++ b/chrome/browser/ui/webui/welcome_handler.h
@@ -37,8 +37,9 @@ void HandleUserDecline(const base::ListValue* args); void GoToNewTabPage(); + Browser* GetBrowser(); + Profile* profile_; - Browser* browser_; ProfileOAuth2TokenService* oauth2_token_service_; WelcomeResult result_;
diff --git a/chrome/browser/win/enumerate_modules_model.cc b/chrome/browser/win/enumerate_modules_model.cc index 376e6b08..7d301ca 100644 --- a/chrome/browser/win/enumerate_modules_model.cc +++ b/chrome/browser/win/enumerate_modules_model.cc
@@ -60,6 +60,12 @@ namespace { +// The default amount of time between module inspections. This prevents +// certificate inspection and validation from using a large amount of CPU and +// battery immediately after startup. +constexpr base::TimeDelta kDefaultPerModuleDelay = + base::TimeDelta::FromSeconds(1); + // A struct to help de-duping modules before adding them to the enumerated // modules vector. struct FindModule { @@ -393,7 +399,8 @@ ModuleEnumerator::ModuleEnumerator(EnumerateModulesModel* observer) : enumerated_modules_(nullptr), - observer_(observer) { + observer_(observer), + per_module_delay_(kDefaultPerModuleDelay) { } ModuleEnumerator::~ModuleEnumerator() { @@ -408,12 +415,17 @@ // scanning has not been finished before shutdown. BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior( FROM_HERE, - base::Bind(&ModuleEnumerator::ScanImpl, + base::Bind(&ModuleEnumerator::ScanImplStart, base::Unretained(this)), base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); } -void ModuleEnumerator::ScanImpl() { +void ModuleEnumerator::SetPerModuleDelayToZero() { + // Set the delay to zero so the modules enumerate as quickly as possible. + per_module_delay_ = base::TimeDelta::FromSeconds(0); +} + +void ModuleEnumerator::ScanImplStart() { base::TimeTicks start_time = base::TimeTicks::Now(); // The provided destination for the enumerated modules should be empty, as it @@ -444,6 +456,61 @@ UMA_HISTOGRAM_TIMES("Conflicts.EnumerateWinsockModules", checkpoint2 - checkpoint); + enumeration_total_time_ = base::TimeTicks::Now() - start_time; + + // Post a delayed task to scan the first module. This forwards directly to + // ScanImplFinish if there are no modules to scan. + BrowserThread::GetBlockingPool()->PostDelayedWorkerTask( + FROM_HERE, + base::Bind(&ModuleEnumerator::ScanImplModule, + base::Unretained(this), + 0), + per_module_delay_); +} + +void ModuleEnumerator::ScanImplDelay(size_t index) { + // Bounce this over to a CONTINUE_ON_SHUTDOWN task in the same pool. This is + // necessary to prevent shutdown hangs while inspecting a module. + BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior( + FROM_HERE, + base::Bind(&ModuleEnumerator::ScanImplModule, + base::Unretained(this), + index), + base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); +} + +void ModuleEnumerator::ScanImplModule(size_t index) { + while (index < enumerated_modules_->size()) { + base::TimeTicks start_time = base::TimeTicks::Now(); + Module& entry = enumerated_modules_->at(index); + PopulateModuleInformation(&entry); + CollapsePath(&entry); + base::TimeDelta elapsed = base::TimeTicks::Now() - start_time; + enumeration_inspection_time_ += elapsed; + enumeration_total_time_ += elapsed; + + // With a non-zero delay, bounce back over to ScanImplDelay, which will + // bounce back to this function and inspect the next module. + if (!per_module_delay_.is_zero()) { + BrowserThread::GetBlockingPool()->PostDelayedWorkerTask( + FROM_HERE, + base::Bind(&ModuleEnumerator::ScanImplDelay, + base::Unretained(this), + index + 1), + per_module_delay_); + return; + } + + // If the delay has been set to zero then simply finish the rest of the + // enumeration in this already started task. + ++index; + } + + // Getting here means that all of the modules have been inspected. + return ScanImplFinish(); +} + +void ModuleEnumerator::ScanImplFinish() { // TODO(chrisha): Annotate any modules that are suspicious/bad. ReportThirdPartyMetrics(); @@ -451,8 +518,10 @@ std::sort(enumerated_modules_->begin(), enumerated_modules_->end(), ModuleSort); + UMA_HISTOGRAM_TIMES("Conflicts.EnumerationInspectionTime", + enumeration_inspection_time_); UMA_HISTOGRAM_TIMES("Conflicts.EnumerationTotalTime", - base::TimeTicks::Now() - start_time); + enumeration_total_time_); // Send a reply back on the UI thread. The |observer_| outlives this // enumerator, so posting a raw pointer is safe. This is done last as @@ -482,10 +551,7 @@ Module entry; entry.type = LOADED_MODULE; entry.location = module.szExePath; - PopulateModuleInformation(&entry); - NormalizeModule(&entry); - CollapsePath(&entry); enumerated_modules_->push_back(entry); } while (::Module32Next(snap.Get(), &module)); } @@ -515,10 +581,7 @@ Module entry; entry.type = SHELL_EXTENSION; entry.location = dll; - PopulateModuleInformation(&entry); - NormalizeModule(&entry); - CollapsePath(&entry); AddToListWithoutDuplicating(entry); ++registration; @@ -542,12 +605,9 @@ wchar_t expanded[MAX_PATH]; DWORD size = ExpandEnvironmentStrings( entry.location.c_str(), expanded, MAX_PATH); - if (size != 0 && size <= MAX_PATH) { - GetCertificateInfo(base::FilePath(expanded), &entry.cert_info); - } + if (size != 0 && size <= MAX_PATH) + entry.location = expanded; entry.version = base::IntToString16(layered_providers[i].version); - - // Paths have already been collapsed. NormalizeModule(&entry); AddToListWithoutDuplicating(entry); } @@ -756,20 +816,29 @@ BrowserThread::PostAfterStartupTask( FROM_HERE, BrowserThread::GetTaskRunnerForThread(BrowserThread::UI), - base::Bind(&EnumerateModulesModel::ScanNow, base::Unretained(this))); + base::Bind(&EnumerateModulesModel::ScanNow, + base::Unretained(this), + true)); done = true; } } -void EnumerateModulesModel::ScanNow() { +void EnumerateModulesModel::ScanNow(bool background_mode) { DCHECK_CURRENTLY_ON(BrowserThread::UI); // |module_enumerator_| is used as a lock to know whether or not there are // active/pending blocking pool tasks. If a module enumerator exists then a // scan is already underway. Otherwise, either no scan has been completed or // a scan has terminated. - if (module_enumerator_) + if (module_enumerator_) { + // If a scan is in progress and this request is for immediate results, then + // inform the background scan. This is done without any locks because the + // other thread only reads from the value that is being modified, and on + // Windows its an atomic write. + if (!background_mode) + module_enumerator_->SetPerModuleDelayToZero(); return; + } // Only allow a single scan per process lifetime. Immediately notify any // observers that the scan is complete. At this point |enumerated_modules_| is @@ -781,6 +850,8 @@ // ScanNow does not block, rather it simply schedules a task. module_enumerator_.reset(new ModuleEnumerator(this)); + if (!background_mode) + module_enumerator_->SetPerModuleDelayToZero(); module_enumerator_->ScanNow(&enumerated_modules_); }
diff --git a/chrome/browser/win/enumerate_modules_model.h b/chrome/browser/win/enumerate_modules_model.h index 5b3beac..045e05c 100644 --- a/chrome/browser/win/enumerate_modules_model.h +++ b/chrome/browser/win/enumerate_modules_model.h
@@ -155,28 +155,51 @@ // will notify when done by calling the DoneScanning method of |observer_|. void ScanNow(ModulesVector* list); + // Sets |per_module_delay_| to zero, causing the modules to be inspected + // in realtime. + void SetPerModuleDelayToZero(); + private: FRIEND_TEST_ALL_PREFIXES(EnumerateModulesTest, CollapsePath); - // This function does the actual file scanning work in the blocking pool. It - // enumerates all loaded modules in the process and other modules of interest, - // such as the registered Winsock LSP modules and stores them in - // |enumerated_modules_|. It then normalizes the module info and matches them - // against a blacklist of known bad modules. Finally, notifies the observer - // that the enumeration is complete by invoking DoneScanning. - void ScanImpl(); + // This function enumerates all modules in the blocking pool. Once the list of + // module filenames is populated it posts a delayed task to call + // ScanImplDelay for the first module. + void ScanImplStart(); - // Enumerate all modules loaded into the Chrome process. + // Immediately posts a CONTINUE_ON_SHUTDOWN task to ScanImplModule for the + // given module. This ping-ponging is because the blocking pool does not + // offer a delayed CONTINUE_ON_SHUTDOWN task. + // TODO(chrisha): When the new scheduler enables delayed CONTINUE_ON_SHUTDOWN + // tasks, simplify this logic. + void ScanImplDelay(size_t index); + + // Inspects the module in |enumerated_modules_| at the given |index|. Gets + // module information, normalizes it, and collapses the path. This is an + // expensive operation and non-critical. Posts a delayed task to ScanImplDelay + // for the next module. When all modules are finished forwards directly to + // ScanImplFinish. + void ScanImplModule(size_t index); + + // Collects metrics and notifies the observer that the enumeration is complete + // by invoking DoneScanning on the UI thread. + void ScanImplFinish(); + + // Enumerate all modules loaded into the Chrome process. Creates empty + // entries in |enumerated_modules_| with a populated |location| field. void EnumerateLoadedModules(); - // Enumerate all registered Windows shell extensions. + // Enumerate all registered Windows shell extensions. Creates empty + // entries in |enumerated_modules_| with a populated |location| field. void EnumerateShellExtensions(); - // Enumerate all registered Winsock LSP modules. + // Enumerate all registered Winsock LSP modules. Creates empty + // entries in |enumerated_modules_| with a populated |location| field. void EnumerateWinsockModules(); // Reads the registered shell extensions found under |parent| key in the - // registry. + // registry. Creates empty entries in |enumerated_modules_| with a populated + // |location| field. void ReadShellExtensions(HKEY parent); // Given a |module|, initializes the structure and loads additional @@ -202,8 +225,7 @@ void CollapsePath(Module* module); // Reports (via UMA) a handful of high-level metrics regarding third party - // modules in this process. Called by ScanImpl after modules have been - // enumerated and processed. + // modules in this process. Called by ScanImplFinish. void ReportThirdPartyMetrics(); // The typedef for the vector that maps a regular file path to %env_var%. @@ -220,6 +242,19 @@ // The observer, which needs to be notified when the scan is complete. EnumerateModulesModel* observer_; + // The delay that is observed between module inspection tasks. This is + // currently 1 second, which means it takes several minutes to iterate over + // all modules on average. + base::TimeDelta per_module_delay_; + + // The amount of time taken for on-disk module inspection. Reported in + // ScanImplFinish. + base::TimeDelta enumeration_inspection_time_; + + // The total amount of time taken for module enumeration. Reported in + // ScanImplFinish. + base::TimeDelta enumeration_total_time_; + DISALLOW_COPY_AND_ASSIGN(ModuleEnumerator); }; @@ -295,10 +330,14 @@ int modules_to_notify_about() const; // Checks to see if a scanning task should be started and sets one off, if so. + // This will cause ScanNow to be invoked in background mode. void MaybePostScanningTask(); - // Asynchronously start the scan for the loaded module list. - void ScanNow(); + // Asynchronously start the scan for the loaded module list. If + // |background_mode| is true the scan will happen slowly over a process of + // minutes, spread across dozens or even hundreds of delayed tasks. Otherwise + // the processing will occur in a single task. + void ScanNow(bool background_mode); // Gets the whole module list as a ListValue. base::ListValue* GetModuleList();
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index 14d9ffd..ae6adbb 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -108,6 +108,13 @@ const base::Feature kMaterialDesignSettings{ "MaterialDesignSettings", base::FEATURE_DISABLED_BY_DEFAULT}; +// Enables the use of native notification centers instead of using the Message +// Center for displaying the toasts. +#if defined(OS_MACOSX) +const base::Feature kNativeNotifications{"NativeNotifications", + base::FEATURE_DISABLED_BY_DEFAULT}; +#endif // defined(OS_MACOSX) + #if defined(ENABLE_PLUGINS) // Prefer HTML content by hiding Flash from the list of plugins. // https://crbug.com/626728
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h index 887f422..a24f65f 100644 --- a/chrome/common/chrome_features.h +++ b/chrome/common/chrome_features.h
@@ -61,6 +61,10 @@ extern const base::Feature kMaterialDesignSettings; +#if defined(OS_MACOSX) +extern const base::Feature kNativeNotifications; +#endif // defined(OS_MACOSX) + #if defined(ENABLE_PLUGINS) extern const base::Feature kPreferHtmlOverPlugins; #endif
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 229a3194..b01eed8 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc
@@ -391,10 +391,6 @@ // (internally adds lEnableGpuPlugin to the command line). const char kEnableNaCl[] = "enable-nacl"; -// Enables the use of native notifications instead of using the Chrome based -// ones. -const char kEnableNativeNotifications[] = "enable-native-notifications"; - // Enables tracing for each navigation. It will attempt to trace each navigation // for 10s, until the buffer is full, or until the next navigation. // It only works if a URL was provided by --trace-upload-url.
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 12e9d72..2122084 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h
@@ -124,7 +124,6 @@ extern const char kEnableMaterialDesignFeedback[]; extern const char kEnableMaterialDesignPolicyPage[]; extern const char kEnableNaCl[]; -extern const char kEnableNativeNotifications[]; extern const char kEnableNavigationTracing[]; extern const char kEnableNetBenchmarking[]; extern const char kEnableNewBookmarkApps[];
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc index 8f63dcd..dc35b36 100644 --- a/chrome/common/url_constants.cc +++ b/chrome/common/url_constants.cc
@@ -621,6 +621,7 @@ kChromeUIHistoryHost, kChromeUIInvalidationsHost, kChromeUILocalStateHost, + kChromeUINetExportHost, kChromeUINetInternalsHost, kChromeUINewTabHost, kChromeUIOmniboxHost,
diff --git a/chrome/installer/linux/BUILD.gn b/chrome/installer/linux/BUILD.gn index 1394af3..c9e0e78 100644 --- a/chrome/installer/linux/BUILD.gn +++ b/chrome/installer/linux/BUILD.gn
@@ -5,6 +5,7 @@ import("//build/config/chrome_build.gni") import("//build/config/features.gni") import("//build/config/sanitizers/sanitizers.gni") +import("//build/config/sysroot.gni") import("//build/util/process_version.gni") import("//build/util/version.gni") import("//chrome/process_version_rc_template.gni") # For branding_file_path. @@ -107,10 +108,10 @@ "debian/changelog.template", "debian/control.template", "debian/debian.menu", - "debian/expected_deps_ia32_precise", - "debian/expected_deps_ia32_trusty", - "debian/expected_deps_x64_precise", - "debian/expected_deps_x64_trusty", + "debian/expected_deps_ia32_jessie", + "debian/expected_deps_ia32_wheezy", + "debian/expected_deps_x64_jessie", + "debian/expected_deps_x64_wheezy", "debian/postinst", "debian/postrm", "debian/prerm", @@ -283,6 +284,10 @@ invoker.channel, "-d", branding_path_component, + "-s", + rebase_path(sysroot), + "-e", + rebase_path("//third_party/dpkg-dev"), ] deps = [ ":installer_deps",
diff --git a/chrome/installer/linux/debian/build.sh b/chrome/installer/linux/debian/build.sh index e43c4377..d8c50a67 100755 --- a/chrome/installer/linux/debian/build.sh +++ b/chrome/installer/linux/debian/build.sh
@@ -128,6 +128,8 @@ echo "-o dir package output directory [${OUTPUTDIR}]" echo "-b dir build input directory [${BUILDDIR}]" echo "-d brand either chromium or google_chrome" + echo "-s dir /path/to/sysroot" + echo "-e dir /path/to/dpkg-dev" echo "-h this help message" } @@ -162,7 +164,7 @@ } process_opts() { - while getopts ":o:b:c:a:d:h" OPTNAME + while getopts ":e:s:o:b:c:a:d:h" OPTNAME do case $OPTNAME in o ) @@ -181,6 +183,12 @@ d ) BRANDING="$OPTARG" ;; + s ) + SYSROOT="$OPTARG" + ;; + e ) + DPKG_DEV_DIR="$OPTARG" + ;; h ) usage exit 0 @@ -217,20 +225,21 @@ else TARGETARCH="ia32" fi -if [[ "$(lsb_release -c)" = *"precise" ]]; then - HOST_DISTRO="precise" -elif [[ "$(lsb_release -c)" = *"trusty" ]]; then - HOST_DISTRO="trusty" -else - echo "Debian package can only be build on Ubuntu Precise or Trusty" - exit 1 -fi # call cleanup() on exit trap cleanup 0 process_opts "$@" BUILDDIR=${BUILDDIR:=$(readlink -f "${SCRIPTDIR}/../../../../out/Release")} +if [[ "$(basename ${SYSROOT})" = "debian_wheezy_"*"-sysroot" ]]; then + TARGET_DISTRO="wheezy" +elif [[ "$(basename ${SYSROOT})" = "debian_jessie_"*"-sysroot" ]]; then + TARGET_DISTRO="jessie" +else + echo "Debian package can only be built using the wheezy or jessie sysroot." + exit 1 +fi + source ${BUILDDIR}/installer/common/installer.include get_version_info @@ -256,13 +265,6 @@ # the LSB sub-packages, to avoid pulling in all that stuff that's not installed # by default. -# Need a dummy debian/control file for dpkg-shlibdeps. -DUMMY_STAGING_DIR="${TMPFILEDIR}/dummy_staging" -mkdir "$DUMMY_STAGING_DIR" -cd "$DUMMY_STAGING_DIR" -mkdir debian -touch debian/control - # Generate the dependencies, # TODO(mmoss): This is a workaround for a problem where dpkg-shlibdeps was # resolving deps using some of our build output shlibs (i.e. @@ -272,33 +274,41 @@ # but it seems that we don't currently, so this is the most expediant fix. SAVE_LDLP=${LD_LIBRARY_PATH:-} unset LD_LIBRARY_PATH -DPKG_SHLIB_DEPS=$(dpkg-shlibdeps -O "$BUILDDIR/chrome" | \ - sed 's/^shlibs:Depends=//') +if [ ${TARGETARCH} = "x64" ]; then + SHLIB_ARGS="-l${SYSROOT}/usr/lib/x86_64-linux-gnu" + SHLIB_ARGS="${SHLIB_ARGS} -l${SYSROOT}/lib/x86_64-linux-gnu" +else + SHLIB_ARGS="-l${SYSROOT}/usr/lib/i386-linux-gnu" + SHLIB_ARGS="${SHLIB_ARGS} -l${SYSROOT}/lib/i386-linux-gnu" +fi +SHLIB_ARGS="${SHLIB_ARGS} -l${SYSROOT}/usr/lib" +# TODO(thomasanderson): Unbundle dpkg-shlibdeps once the Precise->Trusty +# transition is complete by reverting CL 2411423002 and applying ps40001. +DPKG_SHLIB_DEPS=$(cd ${SYSROOT} && DPKG_DATADIR=${DPKG_DEV_DIR} \ + perl -I ${DPKG_DEV_DIR}/scripts ${DPKG_DEV_DIR}/scripts/dpkg-shlibdeps.pl \ + ${SHLIB_ARGS:-} -O -e"$BUILDDIR/chrome" | sed 's/^shlibs:Depends=//') if [ -n "$SAVE_LDLP" ]; then LD_LIBRARY_PATH=$SAVE_LDLP fi # Format it nicely and save it for comparison. -# The grep -v is for a duplicate libc6 dep caused by Lucid glibc silliness. -echo "$DPKG_SHLIB_DEPS" | sed 's/, /\n/g' | \ - grep -v '^libc6 (>= 2.3.6-6~)$' | LANG=C sort > actual +echo "$DPKG_SHLIB_DEPS" | sed 's/, /\n/g' | LANG=C sort > actual -# Compare the expected dependency list to the generate list. +# Compare the expected dependency list to the generated list. BAD_DIFF=0 -diff -u "$SCRIPTDIR/expected_deps_${TARGETARCH}_${HOST_DISTRO}" actual || \ +diff -u "$SCRIPTDIR/expected_deps_${TARGETARCH}_${TARGET_DISTRO}" actual || \ BAD_DIFF=1 if [ $BAD_DIFF -ne 0 ] && [ -z "${IGNORE_DEPS_CHANGES:-}" ]; then echo echo "ERROR: Shared library dependencies changed!" echo "If this is intentional, please update:" - echo "chrome/installer/linux/debian/expected_deps_ia32_precise" - echo "chrome/installer/linux/debian/expected_deps_ia32_trusty" - echo "chrome/installer/linux/debian/expected_deps_x64_precise" - echo "chrome/installer/linux/debian/expected_deps_x64_trusty" + echo "chrome/installer/linux/debian/expected_deps_ia32_jessie" + echo "chrome/installer/linux/debian/expected_deps_ia32_wheezy" + echo "chrome/installer/linux/debian/expected_deps_x64_jessie" + echo "chrome/installer/linux/debian/expected_deps_x64_wheezy" echo exit $BAD_DIFF fi -rm -rf "$DUMMY_STAGING_DIR" # Additional dependencies not in the dpkg-shlibdeps output. # ca-certificates: Make sure users have SSL certificates.
diff --git a/chrome/installer/linux/debian/expected_deps_ia32_trusty b/chrome/installer/linux/debian/expected_deps_ia32_jessie similarity index 100% copy from chrome/installer/linux/debian/expected_deps_ia32_trusty copy to chrome/installer/linux/debian/expected_deps_ia32_jessie
diff --git a/chrome/installer/linux/debian/expected_deps_ia32_precise b/chrome/installer/linux/debian/expected_deps_ia32_precise deleted file mode 100644 index a9c3a7a..0000000 --- a/chrome/installer/linux/debian/expected_deps_ia32_precise +++ /dev/null
@@ -1,32 +0,0 @@ -gconf-service -libasound2 (>= 1.0.23) -libatk1.0-0 (>= 1.12.4) -libc6 (>= 2.11) -libcairo2 (>= 1.6.0) -libcups2 (>= 1.4.0) -libdbus-1-3 (>= 1.2.14) -libexpat1 (>= 1.95.8) -libfontconfig1 (>= 2.8.0) -libfreetype6 (>= 2.3.9) -libgcc1 (>= 1:4.1.1) -libgconf-2-4 (>= 2.31.1) -libgdk-pixbuf2.0-0 (>= 2.22.0) -libglib2.0-0 (>= 2.26.0) -libgtk2.0-0 (>= 2.24.0) -libnspr4 (>= 1.8.0.10) -libnss3 (>= 3.12.4) -libpango1.0-0 (>= 1.14.0) -libstdc++6 (>= 4.6) -libx11-6 (>= 2:1.4.99.1) -libx11-xcb1 -libxcb1 (>= 1.6) -libxcomposite1 (>= 1:0.3-1) -libxcursor1 (>> 1.1.2) -libxdamage1 (>= 1:1.1) -libxext6 -libxfixes3 -libxi6 (>= 2:1.2.99.4) -libxrandr2 (>= 2:1.2.99.3) -libxrender1 -libxss1 -libxtst6
diff --git a/chrome/installer/linux/debian/expected_deps_ia32_trusty b/chrome/installer/linux/debian/expected_deps_ia32_wheezy similarity index 100% rename from chrome/installer/linux/debian/expected_deps_ia32_trusty rename to chrome/installer/linux/debian/expected_deps_ia32_wheezy
diff --git a/chrome/installer/linux/debian/expected_deps_x64_trusty b/chrome/installer/linux/debian/expected_deps_x64_jessie similarity index 81% copy from chrome/installer/linux/debian/expected_deps_x64_trusty copy to chrome/installer/linux/debian/expected_deps_x64_jessie index 6e6d44c..87c2fc50 100644 --- a/chrome/installer/linux/debian/expected_deps_x64_trusty +++ b/chrome/installer/linux/debian/expected_deps_x64_jessie
@@ -1,23 +1,23 @@ gconf-service libasound2 (>= 1.0.16) libatk1.0-0 (>= 1.12.4) -libc6 (>= 2.11) +libc6 (>= 2.15) libcairo2 (>= 1.6.0) libcups2 (>= 1.4.0) -libdbus-1-3 (>= 1.2.14) +libdbus-1-3 (>= 1.1.4) libexpat1 (>= 2.0.1) -libfontconfig1 (>= 2.9.0) +libfontconfig1 (>= 2.11) libfreetype6 (>= 2.3.9) libgcc1 (>= 1:4.1.1) -libgconf-2-4 (>= 2.31.1) +libgconf-2-4 (>= 3.2.5) libgdk-pixbuf2.0-0 (>= 2.22.0) -libglib2.0-0 (>= 2.26.0) +libglib2.0-0 (>= 2.41.1) libgtk2.0-0 (>= 2.24.0) libnspr4 (>= 2:4.9-2~) | libnspr4-0d (>= 1.8.0.10) libnss3 (>= 2:3.13.4-2~) | libnss3-1d (>= 3.12.4) libpango-1.0-0 (>= 1.14.0) libpangocairo-1.0-0 (>= 1.14.0) -libstdc++6 (>= 4.6) +libstdc++6 (>= 4.8.1) libx11-6 (>= 2:1.4.99.1) libx11-xcb1 libxcb1 (>= 1.6)
diff --git a/chrome/installer/linux/debian/expected_deps_x64_precise b/chrome/installer/linux/debian/expected_deps_x64_precise deleted file mode 100644 index a5f66cb..0000000 --- a/chrome/installer/linux/debian/expected_deps_x64_precise +++ /dev/null
@@ -1,32 +0,0 @@ -gconf-service -libasound2 (>= 1.0.23) -libatk1.0-0 (>= 1.12.4) -libc6 (>= 2.11) -libcairo2 (>= 1.6.0) -libcups2 (>= 1.4.0) -libdbus-1-3 (>= 1.1.4) -libexpat1 (>= 1.95.8) -libfontconfig1 (>= 2.8.0) -libfreetype6 (>= 2.3.9) -libgcc1 (>= 1:4.1.1) -libgconf-2-4 (>= 2.31.1) -libgdk-pixbuf2.0-0 (>= 2.22.0) -libglib2.0-0 (>= 2.26.0) -libgtk2.0-0 (>= 2.24.0) -libnspr4 (>= 1.8.0.10) -libnss3 (>= 3.12.4) -libpango1.0-0 (>= 1.14.0) -libstdc++6 (>= 4.6) -libx11-6 (>= 2:1.4.99.1) -libx11-xcb1 -libxcb1 (>= 1.6) -libxcomposite1 (>= 1:0.3-1) -libxcursor1 (>> 1.1.2) -libxdamage1 (>= 1:1.1) -libxext6 -libxfixes3 -libxi6 (>= 2:1.2.99.4) -libxrandr2 (>= 2:1.2.99.3) -libxrender1 -libxss1 -libxtst6
diff --git a/chrome/installer/linux/debian/expected_deps_x64_trusty b/chrome/installer/linux/debian/expected_deps_x64_wheezy similarity index 88% rename from chrome/installer/linux/debian/expected_deps_x64_trusty rename to chrome/installer/linux/debian/expected_deps_x64_wheezy index 6e6d44c..b23857d 100644 --- a/chrome/installer/linux/debian/expected_deps_x64_trusty +++ b/chrome/installer/linux/debian/expected_deps_x64_wheezy
@@ -4,7 +4,7 @@ libc6 (>= 2.11) libcairo2 (>= 1.6.0) libcups2 (>= 1.4.0) -libdbus-1-3 (>= 1.2.14) +libdbus-1-3 (>= 1.1.4) libexpat1 (>= 2.0.1) libfontconfig1 (>= 2.9.0) libfreetype6 (>= 2.3.9) @@ -15,8 +15,7 @@ libgtk2.0-0 (>= 2.24.0) libnspr4 (>= 2:4.9-2~) | libnspr4-0d (>= 1.8.0.10) libnss3 (>= 2:3.13.4-2~) | libnss3-1d (>= 3.12.4) -libpango-1.0-0 (>= 1.14.0) -libpangocairo-1.0-0 (>= 1.14.0) +libpango1.0-0 (>= 1.14.0) libstdc++6 (>= 4.6) libx11-6 (>= 2:1.4.99.1) libx11-xcb1
diff --git a/chrome/test/data/webui/settings/appearance_page_test.js b/chrome/test/data/webui/settings/appearance_page_test.js index 4ade4fc..49b4909 100644 --- a/chrome/test/data/webui/settings/appearance_page_test.js +++ b/chrome/test/data/webui/settings/appearance_page_test.js
@@ -15,6 +15,7 @@ var TestAppearanceBrowserProxy = function() { settings.TestBrowserProxy.call(this, [ 'getThemeInfo', + 'isSupervised', 'openWallpaperManager', 'useDefaultTheme', 'useSystemTheme', @@ -24,6 +25,9 @@ TestAppearanceBrowserProxy.prototype = { __proto__: settings.TestBrowserProxy.prototype, + /** @private */ + isSupervised_: false, + /** @override */ getThemeInfo: function(themeId) { this.methodCalled('getThemeInfo', themeId); @@ -31,6 +35,12 @@ }, /** @override */ + isSupervised: function() { + this.methodCalled('isSupervised'); + return this.isSupervised_; + }, + + /** @override */ openWallpaperManager: function() { this.methodCalled('openWallpaperManager'); }, @@ -44,6 +54,11 @@ useSystemTheme: function() { this.methodCalled('useSystemTheme'); }, + + /** @param {boolean} Whether the user is supervised */ + setIsSupervised: function(isSupervised) { + this.isSupervised_ = isSupervised; + }, }; /** @@ -105,7 +120,7 @@ extensions: { theme: { id: { - value: 'asdf', + value: '', }, use_system: { value: false, @@ -134,20 +149,78 @@ }); } - test('useDefaultTheme', function() { - var button = appearancePage.$$('#useDefault'); - assertTrue(!!button); - MockInteractions.tap(button); - return appearanceBrowserProxy.whenCalled('useDefaultTheme'); - }); + var THEME_ID_PREF = 'prefs.extensions.theme.id.value'; if (cr.isLinux && !cr.isChromeOS) { - test('useSystemTheme', function() { + var USE_SYSTEM_PREF = 'prefs.extensions.theme.use_system.value'; + + test('useDefaultThemeLinux', function() { + assertFalse(!!appearancePage.get(THEME_ID_PREF)); + assertFalse(appearancePage.get(USE_SYSTEM_PREF)); + // No custom nor system theme in use; "USE CLASSIC" should be hidden. + assertFalse(!!appearancePage.$$('#useDefault')); + + appearancePage.set(USE_SYSTEM_PREF, true); + Polymer.dom.flush(); + // If the system theme is in use, "USE CLASSIC" should show. + assertTrue(!!appearancePage.$$('#useDefault')); + + appearancePage.set(USE_SYSTEM_PREF, false); + appearancePage.set(THEME_ID_PREF, 'fake theme id'); + Polymer.dom.flush(); + + // With a custom theme installed, "USE CLASSIC" should show. + var button = appearancePage.$$('#useDefault'); + assertTrue(!!button); + + MockInteractions.tap(button); + return appearanceBrowserProxy.whenCalled('useDefaultTheme'); + }); + + test('useSystemThemeLinux', function() { + assertFalse(!!appearancePage.get(THEME_ID_PREF)); + appearancePage.set(USE_SYSTEM_PREF, true); + Polymer.dom.flush(); + // The "USE GTK+" button shouldn't be showing if it's already in use. + assertFalse(!!appearancePage.$$('#useSystem')); + + appearanceBrowserProxy.setIsSupervised(true); + appearancePage.set(USE_SYSTEM_PREF, false); + Polymer.dom.flush(); + // Supervised users have their own theme and can't use GTK+ theme. + assertFalse(!!appearancePage.$$('#useDefault')); + assertFalse(!!appearancePage.$$('#useSystem')); + // If there's no "USE" buttons, the container should be hidden. + assertTrue(appearancePage.$$('.secondary-action').hidden); + + appearanceBrowserProxy.setIsSupervised(false); + appearancePage.set(THEME_ID_PREF, 'fake theme id'); + Polymer.dom.flush(); + // If there's "USE" buttons again, the container should be visible. + assertTrue(!!appearancePage.$$('#useDefault')); + assertFalse(appearancePage.$$('.secondary-action').hidden); + var button = appearancePage.$$('#useSystem'); assertTrue(!!button); + MockInteractions.tap(button); return appearanceBrowserProxy.whenCalled('useSystemTheme'); }); + } else { + test('useDefaultTheme', function() { + assertFalse(!!appearancePage.get(THEME_ID_PREF)); + assertFalse(!!appearancePage.$$('#useDefault')); + + appearancePage.set(THEME_ID_PREF, 'fake theme id'); + Polymer.dom.flush(); + + // With a custom theme installed, "RESET TO DEFAULT" should show. + var button = appearancePage.$$('#useDefault'); + assertTrue(!!button); + + MockInteractions.tap(button); + return appearanceBrowserProxy.whenCalled('useDefaultTheme'); + }); } }); }
diff --git a/chrome/test/data/webui/settings/settings_autofill_section_browsertest.js b/chrome/test/data/webui/settings/settings_autofill_section_browsertest.js index c3b7c61..988b092d 100644 --- a/chrome/test/data/webui/settings/settings_autofill_section_browsertest.js +++ b/chrome/test/data/webui/settings/settings_autofill_section_browsertest.js
@@ -9,8 +9,7 @@ // Polymer BrowserTest fixture. GEN_INCLUDE([ - ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js', - ROOT_PATH + 'ui/webui/resources/js/load_time_data.js', + ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js', ]); /** @@ -24,9 +23,9 @@ getCountryList: function() { return new Promise(function(resolve) { resolve([ - {name: 'United States', countryCode: 'US'}, // Default test country. - {name: 'Israel', countryCode: 'IL'}, - {name: 'United Kingdom', countryCode: 'GB'}, + {name: 'United States', countryCode: 'US'}, // Default test country. + {name: 'Israel', countryCode: 'IL'}, + {name: 'United Kingdom', countryCode: 'GB'}, ]); }); }, @@ -47,7 +46,7 @@ * @return {!Promise} */ function asyncForEach(items, loopBody) { - return new Promise(function(finish) { + return new Promise(function(resolve) { var index = 0; function loop() { @@ -55,7 +54,7 @@ if (item) loopBody(item).then(loop); else - finish(); + resolve(); }; loop(); @@ -108,13 +107,6 @@ */ runAccessibilityChecks: false, - i18nStrings: { - addAddressTitle: 'add-title', - addCreditCardTitle: 'add-title', - editAddressTitle: 'edit-title', - editCreditCardTitle: 'edit-title', - }, - /** @override */ setUp: function() { PolymerTest.prototype.setUp.call(this); @@ -122,9 +114,6 @@ // Test is run on an individual element that won't have a page language. this.accessibilityAuditConfig.auditRulesToIgnore.push('humanLangMissing'); - // Faking 'strings.js' for this test. - loadTimeData.data = this.i18nStrings; - settings.address.CountryDetailManagerImpl.instance_ = new CountryDetailManagerTestImpl(); }, @@ -410,7 +399,7 @@ return self.createAddressDialog_( FakeDataMaker.emptyAddressEntry()).then(function(dialog) { var title = dialog.$$('.title'); - assertEquals(self.i18nStrings.addAddressTitle, title.textContent); + assertEquals(loadTimeData.getString('addAddress'), title.textContent); // Shouldn't be possible to save until something is typed in. assertTrue(dialog.$.saveButton.disabled); }); @@ -420,7 +409,8 @@ return self.createAddressDialog_( FakeDataMaker.addressEntry()).then(function(dialog) { var title = dialog.$$('.title'); - assertEquals(self.i18nStrings.editAddressTitle, title.textContent); + assertEquals( + loadTimeData.getString('editAddressTitle'), title.textContent); // Should be possible to save when editing because fields are populated. assertFalse(dialog.$.saveButton.disabled); });
diff --git a/chrome/test/data/webui/settings/site_list_tests.js b/chrome/test/data/webui/settings/site_list_tests.js index 72cf641..cf8137c 100644 --- a/chrome/test/data/webui/settings/site_list_tests.js +++ b/chrome/test/data/webui/settings/site_list_tests.js
@@ -401,7 +401,6 @@ assertEquals( settings.PermissionValues.ALLOW, testElement.categorySubtype); - assertEquals('Allow - 0', testElement.$.header.innerText.trim()); assertFalse(testElement.$.category.hidden); browserProxy.resetResolver('getExceptionList'); @@ -409,8 +408,6 @@ return browserProxy.whenCalled('getExceptionList'); }).then(function(contentType) { assertFalse(testElement.$.category.hidden); - assertEquals('Exceptions - 0', - testElement.$.header.innerText.trim()); }); }); @@ -430,7 +427,6 @@ Polymer.dom.flush(); // Populates action menu. openActionMenu(0); assertMenu(['Block', 'Remove'], testElement); - assertEquals('Allow - 2', testElement.$.header.innerText.trim()); // Site list should show, no matter what category default is set // to. @@ -440,8 +436,6 @@ return browserProxy.whenCalled('getExceptionList'); }).then(function(contentType) { assertFalse(testElement.$.category.hidden); - assertEquals('Exceptions - 2', - testElement.$.header.innerText.trim()); }); }); @@ -462,7 +456,6 @@ Polymer.dom.flush(); // Populates action menu. openActionMenu(0); assertMenu(['Allow', 'Remove'], testElement); - assertEquals('Block - 2', testElement.$.header.innerText.trim()); // Site list should only show when category default is enabled. assertFalse(testElement.$.category.hidden); @@ -491,8 +484,6 @@ Polymer.dom.flush(); // Populates action menu. openActionMenu(0); assertMenu(['Allow', 'Block', 'Remove'], testElement); - assertEquals('Clear on exit - 1', - testElement.$.header.innerText.trim()); // Site list should show, no matter what category default is set // to. @@ -502,7 +493,6 @@ return browserProxy.whenCalled('getExceptionList'); }).then(function(contentType) { assertFalse(testElement.$.category.hidden); - assertEquals('Clear on exit - 1', testElement.$.header.innerText); }); }); @@ -523,8 +513,6 @@ openActionMenu(0); // 'Clear on exit' is visible as this is not an incognito item. assertMenu(['Allow', 'Clear on exit', 'Remove'], testElement); - assertEquals('Block - 1', - testElement.$.header.innerText.trim()); // Select 'Remove from menu'. var menuItems = getMenuItems(testElement.$.listContainer, 0); @@ -560,8 +548,6 @@ openActionMenu(0); // 'Clear on exit' is hidden for incognito items. assertMenu(['Block', 'Remove'], testElement); - assertEquals('Allow - 2', - testElement.$.header.innerText.trim()); // Select 'Remove' from menu on 'foo.com'. openActionMenu(1);
diff --git a/chrome/tools/build/win/FILES.cfg b/chrome/tools/build/win/FILES.cfg index 163f0b86..877890e 100644 --- a/chrome/tools/build/win/FILES.cfg +++ b/chrome/tools/build/win/FILES.cfg
@@ -388,7 +388,6 @@ }, { 'filename': 'remoting-me2me-host-win.zip', - 'arch': ['32bit'], 'buildtype': ['dev', 'official'], 'archive': 'remoting-me2me-host-win.zip', 'direct_archive': 1, @@ -396,42 +395,36 @@ }, { 'filename': 'remoting_core.dll', - 'arch': ['32bit'], 'buildtype': ['official'], 'archive': 'remoting-win32.zip', 'filegroup': ['symsrc'], }, { 'filename': 'remoting_core.dll.pdb', - 'arch': ['32bit'], 'buildtype': ['official'], 'archive': 'remoting-win32.zip', 'optional': ['official'], }, { 'filename': 'remoting_desktop.exe', - 'arch': ['32bit'], 'buildtype': ['official'], 'archive': 'remoting-win32.zip', 'filegroup': ['symsrc'], }, { 'filename': 'remoting_desktop.exe.pdb', - 'arch': ['32bit'], 'buildtype': ['official'], 'archive': 'remoting-win32.zip', 'optional': ['official'], }, { 'filename': 'remoting_host.exe', - 'arch': ['32bit'], 'buildtype': ['official'], 'archive': 'remoting-win32.zip', 'filegroup': ['symsrc'], }, { 'filename': 'remoting_host.exe.pdb', - 'arch': ['32bit'], 'buildtype': ['official'], 'archive': 'remoting-win32.zip', }, @@ -605,7 +598,7 @@ 'arch': ['32bit'], 'buildtype': ['dev', 'official'], 'archive': 'chrome-win32-syms.zip', - 'optional': ['dev'], + 'optional': ['dev', 'official'], }, { 'filename': 'syzygy/chrome_child.dll.pdb', @@ -619,7 +612,7 @@ 'arch': ['32bit'], 'buildtype': ['dev', 'official'], 'archive': 'chrome-win32-syms.zip', - 'optional': ['dev'], + 'optional': ['dev', 'official'], }, { 'filename': 'syzygy/syzyasan_rtl.dll.pdb',
diff --git a/chromeos/dbus/debug_daemon_client.cc b/chromeos/dbus/debug_daemon_client.cc index ed83a06b..eda3a10 100644 --- a/chromeos/dbus/debug_daemon_client.cc +++ b/chromeos/dbus/debug_daemon_client.cc
@@ -120,7 +120,7 @@ writer.AppendBool(is_compressed); writer.AppendFileDescriptor(file_descriptor); debugdaemon_proxy_->CallMethod( - &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + &method_call, kBigLogsDBusTimeoutMS, base::Bind(&DebugDaemonClientImpl::OnGetDebugLogs, weak_ptr_factory_.GetWeakPtr(), callback)); }
diff --git a/components/bookmarks/browser/bookmark_model.cc b/components/bookmarks/browser/bookmark_model.cc index d2433bd..a399809 100644 --- a/components/bookmarks/browser/bookmark_model.cc +++ b/components/bookmarks/browser/bookmark_model.cc
@@ -129,8 +129,8 @@ } BookmarkModel::~BookmarkModel() { - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - BookmarkModelBeingDeleted(this)); + for (BookmarkModelObserver& observer : observers_) + observer.BookmarkModelBeingDeleted(this); if (store_.get()) { // The store maintains a reference back to us. We need to tell it we're gone @@ -178,8 +178,8 @@ void BookmarkModel::BeginExtensiveChanges() { if (++extensive_changes_ == 1) { - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - ExtensiveBookmarkChangesBeginning(this)); + for (BookmarkModelObserver& observer : observers_) + observer.ExtensiveBookmarkChangesBeginning(this); } } @@ -187,19 +187,19 @@ --extensive_changes_; DCHECK_GE(extensive_changes_, 0); if (extensive_changes_ == 0) { - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - ExtensiveBookmarkChangesEnded(this)); + for (BookmarkModelObserver& observer : observers_) + observer.ExtensiveBookmarkChangesEnded(this); } } void BookmarkModel::BeginGroupedChanges() { - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - GroupedBookmarkChangesBeginning(this)); + for (BookmarkModelObserver& observer : observers_) + observer.GroupedBookmarkChangesBeginning(this); } void BookmarkModel::EndGroupedChanges() { - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - GroupedBookmarkChangesEnded(this)); + for (BookmarkModelObserver& observer : observers_) + observer.GroupedBookmarkChangesEnded(this); } void BookmarkModel::Remove(const BookmarkNode* node) { @@ -218,8 +218,8 @@ }; std::vector<RemoveNodeData> removed_node_data_list; - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - OnWillRemoveAllUserBookmarks(this)); + for (BookmarkModelObserver& observer : observers_) + observer.OnWillRemoveAllUserBookmarks(this); BeginExtensiveChanges(); // Skip deleting permanent nodes. Permanent bookmark nodes are the root and @@ -244,8 +244,8 @@ if (store_.get()) store_->ScheduleSave(); - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - BookmarkAllUserNodesRemoved(this, removed_urls)); + for (BookmarkModelObserver& observer : observers_) + observer.BookmarkAllUserNodesRemoved(this, removed_urls); BeginGroupedChanges(); for (auto& removed_node_data : removed_node_data_list) { @@ -294,9 +294,8 @@ if (store_.get()) store_->ScheduleSave(); - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - BookmarkNodeMoved(this, old_parent, old_index, - new_parent, index)); + for (BookmarkModelObserver& observer : observers_) + observer.BookmarkNodeMoved(this, old_parent, old_index, new_parent, index); } void BookmarkModel::Copy(const BookmarkNode* node, @@ -352,8 +351,8 @@ return; } - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - OnWillChangeBookmarkNode(this, node)); + for (BookmarkModelObserver& observer : observers_) + observer.OnWillChangeBookmarkNode(this, node); // The title index doesn't support changing the title, instead we remove then // add it back. @@ -364,8 +363,8 @@ if (store_.get()) store_->ScheduleSave(); - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - BookmarkNodeChanged(this, node)); + for (BookmarkModelObserver& observer : observers_) + observer.BookmarkNodeChanged(this, node); } void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) { @@ -378,8 +377,8 @@ mutable_node->InvalidateFavicon(); CancelPendingFaviconLoadRequests(mutable_node); - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - OnWillChangeBookmarkNode(this, node)); + for (BookmarkModelObserver& observer : observers_) + observer.OnWillChangeBookmarkNode(this, node); { base::AutoLock url_lock(url_lock_); @@ -391,8 +390,8 @@ if (store_.get()) store_->ScheduleSave(); - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - BookmarkNodeChanged(this, node)); + for (BookmarkModelObserver& observer : observers_) + observer.BookmarkNodeChanged(this, node); } void BookmarkModel::SetNodeMetaInfo(const BookmarkNode* node, @@ -402,14 +401,14 @@ if (node->GetMetaInfo(key, &old_value) && old_value == value) return; - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - OnWillChangeBookmarkMetaInfo(this, node)); + for (BookmarkModelObserver& observer : observers_) + observer.OnWillChangeBookmarkMetaInfo(this, node); if (AsMutable(node)->SetMetaInfo(key, value) && store_.get()) store_->ScheduleSave(); - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - BookmarkMetaInfoChanged(this, node)); + for (BookmarkModelObserver& observer : observers_) + observer.BookmarkMetaInfoChanged(this, node); } void BookmarkModel::SetNodeMetaInfoMap( @@ -420,15 +419,15 @@ (old_meta_info_map && meta_info_map == *old_meta_info_map)) return; - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - OnWillChangeBookmarkMetaInfo(this, node)); + for (BookmarkModelObserver& observer : observers_) + observer.OnWillChangeBookmarkMetaInfo(this, node); AsMutable(node)->SetMetaInfoMap(meta_info_map); if (store_.get()) store_->ScheduleSave(); - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - BookmarkMetaInfoChanged(this, node)); + for (BookmarkModelObserver& observer : observers_) + observer.BookmarkMetaInfoChanged(this, node); } void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode* node, @@ -437,14 +436,14 @@ if (!meta_info_map || meta_info_map->find(key) == meta_info_map->end()) return; - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - OnWillChangeBookmarkMetaInfo(this, node)); + for (BookmarkModelObserver& observer : observers_) + observer.OnWillChangeBookmarkMetaInfo(this, node); if (AsMutable(node)->DeleteMetaInfo(key) && store_.get()) store_->ScheduleSave(); - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - BookmarkMetaInfoChanged(this, node)); + for (BookmarkModelObserver& observer : observers_) + observer.BookmarkMetaInfoChanged(this, node); } void BookmarkModel::AddNonClonedKey(const std::string& key) { @@ -492,9 +491,8 @@ BookmarkNode* mutable_node = AsMutable(node); mutable_node->InvalidateFavicon(); CancelPendingFaviconLoadRequests(mutable_node); - FOR_EACH_OBSERVER(BookmarkModelObserver, - observers_, - BookmarkNodeFaviconChanged(this, node)); + for (BookmarkModelObserver& observer : observers_) + observer.BookmarkNodeFaviconChanged(this, node); } } @@ -650,8 +648,8 @@ return; } - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - OnWillReorderBookmarkNode(this, parent)); + for (BookmarkModelObserver& observer : observers_) + observer.OnWillReorderBookmarkNode(this, parent); UErrorCode error = U_ZERO_ERROR; std::unique_ptr<icu::Collator> collator(icu::Collator::createInstance(error)); @@ -665,8 +663,8 @@ if (store_.get()) store_->ScheduleSave(); - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - BookmarkNodeChildrenReordered(this, parent)); + for (BookmarkModelObserver& observer : observers_) + observer.BookmarkNodeChildrenReordered(this, parent); } void BookmarkModel::ReorderChildren( @@ -679,8 +677,8 @@ for (const BookmarkNode* node : ordered_nodes) DCHECK_EQ(parent, node->parent()); - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - OnWillReorderBookmarkNode(this, parent)); + for (BookmarkModelObserver& observer : observers_) + observer.OnWillReorderBookmarkNode(this, parent); if (ordered_nodes.size() > 1) { std::map<const BookmarkNode*, int> order; @@ -700,8 +698,8 @@ store_->ScheduleSave(); } - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - BookmarkNodeChildrenReordered(this, parent)); + for (BookmarkModelObserver& observer : observers_) + observer.BookmarkNodeChildrenReordered(this, parent); } void BookmarkModel::SetDateFolderModified(const BookmarkNode* parent, @@ -774,8 +772,8 @@ void BookmarkModel::NotifyNodeAddedForAllDescendents(const BookmarkNode* node) { for (int i = 0; i < node->child_count(); ++i) { - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - BookmarkNodeAdded(this, node, i)); + for (BookmarkModelObserver& observer : observers_) + observer.BookmarkNodeAdded(this, node, i); NotifyNodeAddedForAllDescendents(node->GetChild(i)); } } @@ -900,8 +898,8 @@ FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading6")); // Notify our direct observers. - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - BookmarkModelLoaded(this, details->ids_reassigned())); + for (BookmarkModelObserver& observer : observers_) + observer.BookmarkModelLoaded(this, details->ids_reassigned()); } void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* node_ptr) { @@ -912,8 +910,8 @@ int index = parent->GetIndexOf(node_ptr); DCHECK_NE(-1, index); - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - OnWillRemoveBookmarks(this, parent, index, node_ptr)); + for (BookmarkModelObserver& observer : observers_) + observer.OnWillRemoveBookmarks(this, parent, index, node_ptr); std::set<GURL> removed_urls; { @@ -924,10 +922,8 @@ if (store_.get()) store_->ScheduleSave(); - FOR_EACH_OBSERVER( - BookmarkModelObserver, - observers_, - BookmarkNodeRemoved(this, parent, index, node.get(), removed_urls)); + for (BookmarkModelObserver& observer : observers_) + observer.BookmarkNodeRemoved(this, parent, index, node.get(), removed_urls); undo_delegate()->OnBookmarkNodeRemoved(this, parent, index, std::move(node)); } @@ -989,8 +985,8 @@ AddNodeToInternalMaps(node_ptr); } - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - BookmarkNodeAdded(this, parent, index)); + for (BookmarkModelObserver& observer : observers_) + observer.BookmarkNodeAdded(this, parent, index); return node_ptr; } @@ -1084,8 +1080,8 @@ } void BookmarkModel::FaviconLoaded(const BookmarkNode* node) { - FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, - BookmarkNodeFaviconChanged(this, node)); + for (BookmarkModelObserver& observer : observers_) + observer.BookmarkNodeFaviconChanged(this, node); } void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode* node) {
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc index 81dcf3f3..0a29998 100644 --- a/components/browser_sync/profile_sync_service.cc +++ b/components/browser_sync/profile_sync_service.cc
@@ -529,29 +529,29 @@ } void ProfileSyncService::OnProtocolEvent(const syncer::ProtocolEvent& event) { - FOR_EACH_OBSERVER(ProtocolEventObserver, protocol_event_observers_, - OnProtocolEvent(event)); + for (auto& observer : protocol_event_observers_) + observer.OnProtocolEvent(event); } void ProfileSyncService::OnDirectoryTypeCommitCounterUpdated( syncer::ModelType type, const syncer::CommitCounters& counters) { - FOR_EACH_OBSERVER(syncer::TypeDebugInfoObserver, type_debug_info_observers_, - OnCommitCountersUpdated(type, counters)); + for (auto& observer : type_debug_info_observers_) + observer.OnCommitCountersUpdated(type, counters); } void ProfileSyncService::OnDirectoryTypeUpdateCounterUpdated( syncer::ModelType type, const syncer::UpdateCounters& counters) { - FOR_EACH_OBSERVER(syncer::TypeDebugInfoObserver, type_debug_info_observers_, - OnUpdateCountersUpdated(type, counters)); + for (auto& observer : type_debug_info_observers_) + observer.OnUpdateCountersUpdated(type, counters); } void ProfileSyncService::OnDatatypeStatusCounterUpdated( syncer::ModelType type, const syncer::StatusCounters& counters) { - FOR_EACH_OBSERVER(syncer::TypeDebugInfoObserver, type_debug_info_observers_, - OnStatusCountersUpdated(type, counters)); + for (auto& observer : type_debug_info_observers_) + observer.OnStatusCountersUpdated(type, counters); } void ProfileSyncService::OnDataTypeRequestsSyncStartup(syncer::ModelType type) { @@ -831,17 +831,18 @@ } void ProfileSyncService::NotifyObservers() { - FOR_EACH_OBSERVER(syncer::SyncServiceObserver, observers_, OnStateChanged()); + for (auto& observer : observers_) + observer.OnStateChanged(); } void ProfileSyncService::NotifySyncCycleCompleted() { - FOR_EACH_OBSERVER(syncer::SyncServiceObserver, observers_, - OnSyncCycleCompleted()); + for (auto& observer : observers_) + observer.OnSyncCycleCompleted(); } void ProfileSyncService::NotifyForeignSessionUpdated() { - FOR_EACH_OBSERVER(syncer::SyncServiceObserver, observers_, - OnForeignSessionUpdated()); + for (auto& observer : observers_) + observer.OnForeignSessionUpdated(); } void ProfileSyncService::ClearStaleErrors() { @@ -1329,8 +1330,8 @@ } // Notify listeners that configuration is done. - FOR_EACH_OBSERVER(syncer::SyncServiceObserver, observers_, - OnSyncConfigurationCompleted()); + for (auto& observer : observers_) + observer.OnSyncConfigurationCompleted(); DVLOG(1) << "PSS OnConfigureDone called with status: " << configure_status_; // The possible status values:
diff --git a/components/invalidation/impl/invalidation_logger.cc b/components/invalidation/impl/invalidation_logger.cc index d25d0b1e..cfb5ca0c 100644 --- a/components/invalidation/impl/invalidation_logger.cc +++ b/components/invalidation/impl/invalidation_logger.cc
@@ -33,8 +33,8 @@ } void InvalidationLogger::EmitRegisteredHandlers() { - FOR_EACH_OBSERVER(InvalidationLoggerObserver, observer_list_, - OnRegistrationChange(registered_handlers_)); + for (auto& observer : observer_list_) + observer.OnRegistrationChange(registered_handlers_); } void InvalidationLogger::OnStateChange( @@ -47,10 +47,10 @@ } void InvalidationLogger::EmitState() { - FOR_EACH_OBSERVER(InvalidationLoggerObserver, - observer_list_, - OnStateChange(last_invalidator_state_, - last_invalidator_state_timestamp_)); + for (auto& observer : observer_list_) { + observer.OnStateChange(last_invalidator_state_, + last_invalidator_state_timestamp_); + } } void InvalidationLogger::OnUpdateIds( @@ -73,15 +73,14 @@ ++oid_it) { per_object_invalidation_count[*oid_it] = invalidation_count_[*oid_it]; } - FOR_EACH_OBSERVER(InvalidationLoggerObserver, - observer_list_, - OnUpdateIds(it->first, per_object_invalidation_count)); + for (auto& observer : observer_list_) + observer.OnUpdateIds(it->first, per_object_invalidation_count); } } void InvalidationLogger::OnDebugMessage(const base::DictionaryValue& details) { - FOR_EACH_OBSERVER( - InvalidationLoggerObserver, observer_list_, OnDebugMessage(details)); + for (auto& observer : observer_list_) + observer.OnDebugMessage(details); } void InvalidationLogger::OnInvalidation( @@ -94,8 +93,8 @@ ++it) { invalidation_count_[it->object_id()]++; } - FOR_EACH_OBSERVER( - InvalidationLoggerObserver, observer_list_, OnInvalidation(details)); + for (auto& observer : observer_list_) + observer.OnInvalidation(details); } void InvalidationLogger::EmitContent() {
diff --git a/components/invalidation/impl/invalidator_registrar.cc b/components/invalidation/impl/invalidator_registrar.cc index 903cf72..f972790 100644 --- a/components/invalidation/impl/invalidator_registrar.cc +++ b/components/invalidation/impl/invalidator_registrar.cc
@@ -115,8 +115,8 @@ DVLOG(1) << "New invalidator state: " << InvalidatorStateToString(state_) << " -> " << InvalidatorStateToString(state); state_ = state; - FOR_EACH_OBSERVER(InvalidationHandler, handlers_, - OnInvalidatorStateChange(state)); + for (auto& observer : handlers_) + observer.OnInvalidatorStateChange(state); } InvalidatorState InvalidatorRegistrar::GetInvalidatorState() const {
diff --git a/components/invalidation/impl/sync_system_resources.cc b/components/invalidation/impl/sync_system_resources.cc index f4f04e0..3cd65e7 100644 --- a/components/invalidation/impl/sync_system_resources.cc +++ b/components/invalidation/impl/sync_system_resources.cc
@@ -193,8 +193,8 @@ void SyncNetworkChannel::NotifyChannelStateChange( InvalidatorState invalidator_state) { - FOR_EACH_OBSERVER(Observer, observers_, - OnNetworkChannelStateChanged(invalidator_state)); + for (auto& observer : observers_) + observer.OnNetworkChannelStateChanged(invalidator_state); } bool SyncNetworkChannel::DeliverIncomingMessage(const std::string& message) {
diff --git a/components/invalidation/impl/ticl_settings_provider.cc b/components/invalidation/impl/ticl_settings_provider.cc index 6c9c632..c8b7adc 100644 --- a/components/invalidation/impl/ticl_settings_provider.cc +++ b/components/invalidation/impl/ticl_settings_provider.cc
@@ -24,7 +24,8 @@ } void TiclSettingsProvider::FireOnUseGCMChannelChanged() { - FOR_EACH_OBSERVER(Observer, observers_, OnUseGCMChannelChanged()); + for (auto& observer : observers_) + observer.OnUseGCMChannelChanged(); } } // namespace invalidation
diff --git a/components/metrics/leak_detector/call_stack_table.cc b/components/metrics/leak_detector/call_stack_table.cc index 8f9540bc0..ec9b7869 100644 --- a/components/metrics/leak_detector/call_stack_table.cc +++ b/components/metrics/leak_detector/call_stack_table.cc
@@ -42,7 +42,7 @@ CallStackTable::~CallStackTable() {} void CallStackTable::Add(const CallStack* call_stack) { - ++entry_map_[call_stack]; + ++entry_map_[call_stack].count; ++num_allocs_; } @@ -50,7 +50,7 @@ auto iter = entry_map_.find(call_stack); if (iter == entry_map_.end()) return; - uint32_t& count_for_call_stack = iter->second; + uint32_t& count_for_call_stack = iter->second.count; --count_for_call_stack; ++num_frees_; @@ -67,9 +67,40 @@ } void CallStackTable::GetTopCallStacks(RankedSet* top_entries) const { - for (const auto& call_stack_and_count : entry_map_) { - top_entries->AddCallStack(call_stack_and_count.first, - call_stack_and_count.second); + for (const auto& call_stack_count_info : entry_map_) { + top_entries->AddCallStack(call_stack_count_info.first, + call_stack_count_info.second.count); + } +} + +void CallStackTable::UpdateLastDropInfo(size_t timestamp) { + for (auto& call_stack_and_info : entry_map_) { + auto& count_info = call_stack_and_info.second; + + // If the |previous_count| is 0 then we need to initialize info. + if (count_info.previous_count == 0 || + count_info.count < count_info.previous_count) { + count_info.last_drop_timestamp = timestamp; + count_info.last_drop_count = count_info.count; + } + count_info.previous_count = count_info.count; + } +} + +void CallStackTable::GetLastUptrendInfo( + const CallStack* call_stack, size_t timestamp, size_t* timestamp_delta, + uint32_t* count_delta) const { + const auto& call_stack_count_info_iter = entry_map_.find(call_stack); + + if (call_stack_count_info_iter != entry_map_.end()) { + const auto& count_info = call_stack_count_info_iter->second; + *timestamp_delta = timestamp - count_info.last_drop_timestamp; + *count_delta = count_info.count - count_info.last_drop_count; + } else { + DLOG(WARNING) << "Accessing information about a call stack that has not " + << "been recorded"; + *timestamp_delta = timestamp; + *count_delta = 0; } }
diff --git a/components/metrics/leak_detector/call_stack_table.h b/components/metrics/leak_detector/call_stack_table.h index 77a4b2a..8372716 100644 --- a/components/metrics/leak_detector/call_stack_table.h +++ b/components/metrics/leak_detector/call_stack_table.h
@@ -48,6 +48,21 @@ // must already be initialized with that number. void GetTopCallStacks(RankedSet* top_entries) const; + // Updates information about the last seen drop in the number of allocations + // and sets the |previous_count| for every stored call stack, both inside + // |entry_map_|. It should be called in the analysis phase, before the + // reports are generated, with |timestamp| describing current point in the + // timeline. + void UpdateLastDropInfo(size_t timestamp); + + // Retrieves the change in timestamp and allocation count since the last + // observed drop in the number for allocations for the given call stack + // (or since the oldest kept record for the call stacks if there were no + // drops) + void GetLastUptrendInfo( + const CallStack* call_stack, size_t timestamp, size_t *timestamp_delta, + uint32_t *count_delta) const; + const LeakAnalyzer& leak_analyzer() const { return leak_analyzer_; } size_t size() const { return entry_map_.size(); } @@ -57,6 +72,13 @@ uint32_t num_frees() const { return num_frees_; } private: + struct CallStackCountInfo { + uint32_t count, previous_count, last_drop_count; + size_t last_drop_timestamp; + CallStackCountInfo() : count(0), previous_count(0), last_drop_count(0), + last_drop_timestamp(0) {} + }; + // Total number of allocs and frees in this table. uint32_t num_allocs_; uint32_t num_frees_; @@ -64,13 +86,13 @@ // Hash table containing entries. Uses CustomAllocator to avoid recursive // malloc hook invocation when analyzing allocs and frees. using TableEntryAllocator = - STLAllocator<std::pair<const CallStack* const, uint32_t>, + STLAllocator<std::pair<const CallStack* const, CallStackCountInfo>, CustomAllocator>; // Stores a mapping of each call stack to the number of recorded allocations // made from that call site. base::hash_map<const CallStack*, - uint32_t, + CallStackCountInfo, StoredHash, std::equal_to<const CallStack*>, TableEntryAllocator> entry_map_;
diff --git a/components/metrics/leak_detector/call_stack_table_unittest.cc b/components/metrics/leak_detector/call_stack_table_unittest.cc index f56f628..253b2bc 100644 --- a/components/metrics/leak_detector/call_stack_table_unittest.cc +++ b/components/metrics/leak_detector/call_stack_table_unittest.cc
@@ -360,5 +360,99 @@ EXPECT_EQ(stack2_, iter->value.call_stack()); } +TEST_F(CallStackTableTest, GetLastUptrendInfo) { + CallStackTable table(kDefaultLeakThreshold); + + // Add some |stack0_| and |stack1_|. + for (int i = 0; i < 60; ++i) + table.Add(stack0_); + for (int i = 0; i < 60; ++i) + table.Add(stack1_); + table.UpdateLastDropInfo(100); + + size_t timestamp_delta; + uint32_t count_delta; + table.GetLastUptrendInfo(stack0_, 100, ×tamp_delta, &count_delta); + EXPECT_EQ(0U, timestamp_delta); + EXPECT_EQ(0U, count_delta); + + // Remove |stack0_| and add |stack1_|. + for (int i = 0; i < 30; ++i) + table.Remove(stack0_); + for (int i = 0; i < 30; ++i) + table.Add(stack1_); + table.UpdateLastDropInfo(200); + + table.GetLastUptrendInfo(stack0_, 200, ×tamp_delta, &count_delta); + EXPECT_EQ(0U, timestamp_delta); + EXPECT_EQ(0U, count_delta); + + table.GetLastUptrendInfo(stack1_, 200, ×tamp_delta, &count_delta); + EXPECT_EQ(100U, timestamp_delta); + EXPECT_EQ(30U, count_delta); + + // Check if previous drop of |stack0_| was recorded and introduce |stack2_|. + for (int i = 0; i < 30; ++i) + table.Add(stack0_); + for (int i = 0; i < 60; ++i) + table.Add(stack2_); + table.UpdateLastDropInfo(300); + + table.GetLastUptrendInfo(stack0_, 300, ×tamp_delta, &count_delta); + EXPECT_EQ(100U, timestamp_delta); + EXPECT_EQ(30U, count_delta); + + table.GetLastUptrendInfo(stack2_, 300, ×tamp_delta, &count_delta); + EXPECT_EQ(0U, timestamp_delta); + EXPECT_EQ(0U, count_delta); + + // Introduce more variation between updates. Decrease |stack2_| to 0. + // All the history for |stack2_| should be forgotten. + for (int i = 0; i < 30; ++i) + table.Add(stack0_); + for (int i = 0; i < 40; ++i) + table.Remove(stack0_); + for (int i = 0; i < 40; ++i) + table.Add(stack1_); + for (int i = 0; i < 30; ++i) + table.Remove(stack1_); + for (int i = 0; i < 30; ++i) + table.Remove(stack2_); + for (int i = 0; i < 30; ++i) + table.Remove(stack2_); + table.UpdateLastDropInfo(400); + + table.GetLastUptrendInfo(stack0_, 400, ×tamp_delta, &count_delta); + EXPECT_EQ(0U, timestamp_delta); + EXPECT_EQ(0U, count_delta); + + table.GetLastUptrendInfo(stack1_, 400, ×tamp_delta, &count_delta); + EXPECT_EQ(300U, timestamp_delta); + EXPECT_EQ(40U, count_delta); + + table.GetLastUptrendInfo(stack2_, 400, ×tamp_delta, &count_delta); + EXPECT_EQ(400U, timestamp_delta); + EXPECT_EQ(0U, count_delta); + + // Make a 0-sum sequence for |stack0_|. Introduce |stack2_| again. + for (int i = 0; i < 30; ++i) + table.Add(stack0_); + for (int i = 0; i < 30; ++i) + table.Remove(stack0_); + for (int i = 0; i < 40; ++i) + table.Add(stack2_); + for (int i = 0; i < 30; ++i) + table.Remove(stack2_); + table.UpdateLastDropInfo(500); + + table.GetLastUptrendInfo(stack0_, 500, ×tamp_delta, &count_delta); + EXPECT_EQ(100U, timestamp_delta); + EXPECT_EQ(0U, count_delta); + + table.GetLastUptrendInfo(stack2_, 500, ×tamp_delta, &count_delta); + EXPECT_EQ(0U, timestamp_delta); + EXPECT_EQ(0U, count_delta); +} + } // namespace leak_detector } // namespace metrics
diff --git a/components/metrics/leak_detector/leak_detector.cc b/components/metrics/leak_detector/leak_detector.cc index 5f33bab..6039e12 100644 --- a/components/metrics/leak_detector/leak_detector.cc +++ b/components/metrics/leak_detector/leak_detector.cc
@@ -121,6 +121,9 @@ proto_entry->set_count_for_call_stack(entry.count_for_call_stack); } + proto.set_num_rising_intervals(report.num_rising_intervals()); + proto.set_num_allocs_increase(report.num_allocs_increase()); + return proto; } @@ -272,8 +275,14 @@ detector->last_analysis_alloc_size_ = total_alloc_size - total_alloc_size % analysis_interval_bytes; + // Collect new leak reports. Use current |total_alloc_size| as + // a timestamp, then transform change in timestamp into the + // number of intervals passed. InternalVector<InternalLeakReport> leak_reports; - detector->impl_->TestForLeaks(&leak_reports); + detector->impl_->TestForLeaks(&leak_reports, total_alloc_size); + for (auto &report : leak_reports) + report.set_num_rising_intervals( + report.num_rising_intervals() / analysis_interval_bytes); // Pass leak reports to observers. std::vector<MemoryLeakReportProto> leak_report_protos;
diff --git a/components/metrics/leak_detector/leak_detector.mojom b/components/metrics/leak_detector/leak_detector.mojom index d985b79..08eea5119 100644 --- a/components/metrics/leak_detector/leak_detector.mojom +++ b/components/metrics/leak_detector/leak_detector.mojom
@@ -20,6 +20,8 @@ struct MemoryLeakReport { uint32 size_bytes; array<uint64> call_stack; + uint32 num_rising_intervals; + uint32 num_allocs_increase; array<AllocationBreakdown> alloc_breakdown_history; };
diff --git a/components/metrics/leak_detector/leak_detector_impl.cc b/components/metrics/leak_detector/leak_detector_impl.cc index 5fd4653..43bd0d24 100644 --- a/components/metrics/leak_detector/leak_detector_impl.cc +++ b/components/metrics/leak_detector/leak_detector_impl.cc
@@ -173,7 +173,8 @@ address_map_.erase(iter); } -void LeakDetectorImpl::TestForLeaks(InternalVector<LeakReport>* reports) { +void LeakDetectorImpl::TestForLeaks(InternalVector<LeakReport>* reports, + size_t timestamp) { // Add net alloc counts for each size to a ranked list. RankedSet size_ranked_set(kRankedSetSize); for (size_t i = 0; i < size_entries_.size(); ++i) { @@ -183,7 +184,7 @@ } size_leak_analyzer_.AddSample(std::move(size_ranked_set)); - RecordCurrentAllocationDataInHistory(); + RecordCurrentAllocationDataInHistory(timestamp); UpdateLeakCooldowns(); @@ -230,7 +231,7 @@ report->call_stack_[j] = GetOffset(call_stack->stack[j]); } - StoreHistoricalDataInReport(size, call_stack, report); + StoreHistoricalDataInReport(size, call_stack, report, timestamp); ResetLeakCooldown(size, call_stack); } } @@ -253,7 +254,7 @@ return UINTPTR_MAX; } -void LeakDetectorImpl::RecordCurrentAllocationDataInHistory() { +void LeakDetectorImpl::RecordCurrentAllocationDataInHistory(size_t timestamp) { // Record a snapshot of the current size table. InternalVector<uint32_t> current_size_table_record; current_size_table_record.reserve(kNumSizeEntriesInHistory); @@ -273,6 +274,7 @@ continue; RankedSet top_call_stacks(kNumTopCallStacksInHistory); entry.stack_table->GetTopCallStacks(&top_call_stacks); + entry.stack_table->UpdateLastDropInfo(timestamp); entry.call_site_breakdown_history.push_back(std::move(top_call_stacks)); if (entry.call_site_breakdown_history.size() > kMaxNumHistoryEntries) entry.call_site_breakdown_history.pop_front(); @@ -281,7 +283,8 @@ void LeakDetectorImpl::StoreHistoricalDataInReport(size_t size, const CallStack* call_site, - LeakReport* report) { + LeakReport* report, + size_t timestamp) { using AllocationBreakdown = LeakReport::AllocationBreakdown; // Copy historical allocation data into the report. InternalVector<AllocationBreakdown>* dest = &report->alloc_breakdown_history_; @@ -317,6 +320,10 @@ ++src_iter; ++dest_iter; } + + size_entries_[SizeToIndex(size)].stack_table->GetLastUptrendInfo( + call_site, timestamp, &report->num_rising_intervals_, + &report->num_allocs_increase_); } bool LeakDetectorImpl::ReadyToGenerateReport(
diff --git a/components/metrics/leak_detector/leak_detector_impl.h b/components/metrics/leak_detector/leak_detector_impl.h index bf6a58e..65c93d978 100644 --- a/components/metrics/leak_detector/leak_detector_impl.h +++ b/components/metrics/leak_detector/leak_detector_impl.h
@@ -67,6 +67,14 @@ return alloc_breakdown_history_; } + size_t num_rising_intervals() const { return num_rising_intervals_; } + + uint32_t num_allocs_increase() const { return num_allocs_increase_; } + + void set_num_rising_intervals(size_t num_rising_intervals) { + num_rising_intervals_ = num_rising_intervals; + } + // Used to compare the contents of two leak reports. bool operator<(const LeakReport& other) const; @@ -78,6 +86,12 @@ // Number of bytes allocated by the leak site during each allocation. size_t alloc_size_bytes_; + // Number of intervals in the last uptrend. + size_t num_rising_intervals_; + + // Net number of bytes allocated in the last uptrend. + uint32_t num_allocs_increase_; + // Unlike the CallStack struct, which consists of addresses, this call stack // will contain offsets in the executable binary. InternalVector<uintptr_t> call_stack_; @@ -105,7 +119,7 @@ void RecordFree(const void* ptr); // Run check for possible leaks based on the current profiling data. - void TestForLeaks(InternalVector<LeakReport>* reports); + void TestForLeaks(InternalVector<LeakReport>* reports, size_t timestamp); private: // A record of allocations for a particular size. @@ -160,17 +174,21 @@ // per size is recorded in |size_breakdown_history_|. The net number of allocs // per call site for each size is recorded in // |AllocSizeEntry::call_site_breakdown_history|. + // Argument |timestamp| is used to update information about drops in + // allocation number for each stored call stack. // // Not all the net alloc counts are recorded. And the number of historical // records kept is capped. If adding a new record exceeds that limit, the // oldest record is discarded. See the function definition for more details. - void RecordCurrentAllocationDataInHistory(); + void RecordCurrentAllocationDataInHistory(size_t timestamp); // Store the data collected by RecordCurrentAllocationDataInHistory() in // |*report|. Not all net alloc counts per call site will be stored, only the - // count for size=|size| and made from |call_site|. + // count for size=|size| and made from |call_site|. Also information + // about the last uptrend in net allocations for |size| and |call_site| + // is recorded with help of |timestamp|. void StoreHistoricalDataInReport(size_t size, const CallStack* call_site, - LeakReport* report); + LeakReport* report, size_t timestamp); // Decrements the cooldown counter (value) for each entry in // |cooldowns_per_leak_|. If the cooldown counter reaches 0, the entry is
diff --git a/components/metrics/leak_detector/leak_detector_impl_unittest.cc b/components/metrics/leak_detector/leak_detector_impl_unittest.cc index f1e549b..28874f0b 100644 --- a/components/metrics/leak_detector/leak_detector_impl_unittest.cc +++ b/components/metrics/leak_detector/leak_detector_impl_unittest.cc
@@ -184,7 +184,7 @@ total_alloced_size_ += size; if (total_alloced_size_ >= next_analysis_total_alloced_size_) { InternalVector<InternalLeakReport> reports; - detector_->TestForLeaks(&reports); + detector_->TestForLeaks(&reports, 1024); for (const InternalLeakReport& report : reports) { auto iter = stored_reports_.find(report); if (iter == stored_reports_.end()) {
diff --git a/components/metrics/leak_detector/protobuf_to_mojo_converter.cc b/components/metrics/leak_detector/protobuf_to_mojo_converter.cc index 24202dbc..8d18567 100644 --- a/components/metrics/leak_detector/protobuf_to_mojo_converter.cc +++ b/components/metrics/leak_detector/protobuf_to_mojo_converter.cc
@@ -31,6 +31,8 @@ void ReportToMojo(const MemoryLeakReportProto& report, mojom::MemoryLeakReport* mojo_report) { mojo_report->size_bytes = report.size_bytes(); + mojo_report->num_rising_intervals = report.num_rising_intervals(); + mojo_report->num_allocs_increase = report.num_allocs_increase(); for (auto call_stack_value : report.call_stack()) { mojo_report->call_stack.push_back(call_stack_value); } @@ -50,6 +52,8 @@ void MojoToReport(const mojom::MemoryLeakReport& mojo_report, MemoryLeakReportProto* report) { report->set_size_bytes(mojo_report.size_bytes); + report->set_num_rising_intervals(mojo_report.num_rising_intervals); + report->set_num_allocs_increase(mojo_report.num_allocs_increase); for (auto call_stack_addr : mojo_report.call_stack) report->add_call_stack(call_stack_addr);
diff --git a/components/metrics/leak_detector/protobuf_to_mojo_converter_unittest.cc b/components/metrics/leak_detector/protobuf_to_mojo_converter_unittest.cc index f768c31..2ba95a5 100644 --- a/components/metrics/leak_detector/protobuf_to_mojo_converter_unittest.cc +++ b/components/metrics/leak_detector/protobuf_to_mojo_converter_unittest.cc
@@ -45,6 +45,8 @@ report.add_call_stack(0xc001d00d); report.add_call_stack(0x900df00d); report.set_size_bytes(24); + report.set_num_rising_intervals(5); + report.set_num_allocs_increase(42); auto entry1 = report.add_alloc_breakdown_history(); entry1->add_counts_by_size(1); @@ -77,6 +79,8 @@ EXPECT_EQ(0xc001d00d, mojo_report->call_stack[1]); EXPECT_EQ(0x900df00d, mojo_report->call_stack[2]); EXPECT_EQ(24U, mojo_report->size_bytes); + EXPECT_EQ(5U, mojo_report->num_rising_intervals); + EXPECT_EQ(42U, mojo_report->num_allocs_increase); ASSERT_EQ(3U, mojo_report->alloc_breakdown_history.size()); @@ -110,6 +114,8 @@ EXPECT_EQ(0xc001d00d, new_report.call_stack(1)); EXPECT_EQ(0x900df00d, new_report.call_stack(2)); EXPECT_EQ(24U, new_report.size_bytes()); + EXPECT_EQ(5U, new_report.num_rising_intervals()); + EXPECT_EQ(42U, new_report.num_allocs_increase()); ASSERT_EQ(3, new_report.alloc_breakdown_history().size());
diff --git a/components/metrics/proto/memory_leak_report.proto b/components/metrics/proto/memory_leak_report.proto index 1a998fe..2e3d7b7 100644 --- a/components/metrics/proto/memory_leak_report.proto +++ b/components/metrics/proto/memory_leak_report.proto
@@ -109,7 +109,9 @@ // // |num_rising_intervals| equals timeslot_now - timeslot_drop, // where timeslot_drop is the timeslot number of the last frame that saw - // a drop in the number of allocations (or 0 if there were no drops). + // a drop in the number of allocations, or the first frame in the history + // if there were no drops (history is cleared when the net number of + // allocations hits 0). // If it is < 32, it will be visible in the allocation history graph. // If it is >= 32, it will not be seen in the graph. // E.g. for history [3,2,4,4,7] |num_rising_intervals| equals 3.
diff --git a/components/network_time/BUILD.gn b/components/network_time/BUILD.gn index c7170ed..11e5c663 100644 --- a/components/network_time/BUILD.gn +++ b/components/network_time/BUILD.gn
@@ -28,6 +28,7 @@ deps = [ ":network_time", + ":network_time_test_support", "//base", "//base/test:test_support", "//components/client_update_protocol", @@ -38,3 +39,18 @@ "//testing/gtest", ] } + +source_set("network_time_test_support") { + testonly = true + sources = [ + "network_time_test_utils.cc", + "network_time_test_utils.h", + ] + + deps = [ + "//base", + "//base/test:test_support", + "//components/variations", + "//testing/gtest", + ] +}
diff --git a/components/network_time/network_time_test_utils.cc b/components/network_time/network_time_test_utils.cc new file mode 100644 index 0000000..63c6e53 --- /dev/null +++ b/components/network_time/network_time_test_utils.cc
@@ -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. + +#include "components/network_time/network_time_test_utils.h" + +#include "base/feature_list.h" +#include "base/memory/ptr_util.h" +#include "base/metrics/field_trial.h" +#include "base/strings/string_number_conversions.h" +#include "base/test/mock_entropy_provider.h" +#include "base/test/scoped_feature_list.h" +#include "components/variations/variations_associated_data.h" + +namespace network_time { + +FieldTrialTest::FieldTrialTest() : ::testing::Test() {} +FieldTrialTest::~FieldTrialTest() {} + +void FieldTrialTest::SetNetworkQueriesWithVariationsService( + bool enable, + float query_probability) { + const std::string kTrialName = "Trial"; + const std::string kGroupName = "group"; + const base::Feature kFeature{"NetworkTimeServiceQuerying", + base::FEATURE_DISABLED_BY_DEFAULT}; + + // Clear all the things. + variations::testing::ClearAllVariationParams(); + + std::map<std::string, std::string> params; + params["RandomQueryProbability"] = base::DoubleToString(query_probability); + params["CheckTimeIntervalSeconds"] = base::Int64ToString(360); + + // There are 3 things here: a FieldTrial, a FieldTrialList, and a + // FeatureList. Don't get confused! The FieldTrial is reference-counted, + // and a reference is held by the FieldTrialList. The FieldTrialList and + // FeatureList are both singletons. The authorized way to reset the former + // for testing is to destruct it (above). The latter, by contrast, should + // should already start in a clean state and can be manipulated via the + // ScopedFeatureList helper class. If this comment was useful to you + // please send me a postcard. + + field_trial_list_.reset(); // Averts a CHECK fail in constructor below. + field_trial_list_.reset( + new base::FieldTrialList(base::MakeUnique<base::MockEntropyProvider>())); + // refcounted, and reference held by field_trial_list_. + base::FieldTrial* trial = base::FieldTrialList::FactoryGetFieldTrial( + kTrialName, 100, kGroupName, 1971, 1, 1, + base::FieldTrial::SESSION_RANDOMIZED, nullptr /* default_group_number */); + ASSERT_TRUE( + variations::AssociateVariationParams(kTrialName, kGroupName, params)); + + std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); + feature_list->RegisterFieldTrialOverride( + kFeature.name, enable ? base::FeatureList::OVERRIDE_ENABLE_FEATURE + : base::FeatureList::OVERRIDE_DISABLE_FEATURE, + trial); + scoped_feature_list_.reset(new base::test::ScopedFeatureList); + scoped_feature_list_->InitWithFeatureList(std::move(feature_list)); +} + +} // namespace network_time
diff --git a/components/network_time/network_time_test_utils.h b/components/network_time/network_time_test_utils.h new file mode 100644 index 0000000..09dee4c --- /dev/null +++ b/components/network_time/network_time_test_utils.h
@@ -0,0 +1,42 @@ +// 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 COMPONENTS_NETWORK_TIME_NETWORK_TIME_TEST_UTILS_H_ +#define COMPONENTS_NETWORK_TIME_NETWORK_TIME_TEST_UTILS_H_ + +#include <map> +#include <memory> + +#include "base/macros.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace test { +class ScopedFeatureList; +}; // namespace test + +class FieldTrialList; +} // namespace base + +namespace network_time { +// A test fixture that allows configuring the network time queries field trial. +class FieldTrialTest : public ::testing::Test { + public: + FieldTrialTest(); + ~FieldTrialTest() override; + + protected: + void SetNetworkQueriesWithVariationsService(bool enable, + float query_probability); + + private: + std::unique_ptr<base::FieldTrialList> field_trial_list_; + std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_; + + DISALLOW_COPY_AND_ASSIGN(FieldTrialTest); +}; + +} // namespace network_time + +#endif // COMPONENTS_NETWORK_TIME_NETWORK_TIME_TEST_UTILS_H_
diff --git a/components/network_time/network_time_tracker_unittest.cc b/components/network_time/network_time_tracker_unittest.cc index 312b4cd..408425ec 100644 --- a/components/network_time/network_time_tracker_unittest.cc +++ b/components/network_time/network_time_tracker_unittest.cc
@@ -4,26 +4,20 @@ #include "components/network_time/network_time_tracker.h" -#include <map> #include <memory> #include <string> #include <utility> #include "base/compiler_specific.h" -#include "base/feature_list.h" #include "base/memory/ptr_util.h" -#include "base/metrics/field_trial.h" -#include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/test/histogram_tester.h" -#include "base/test/mock_entropy_provider.h" -#include "base/test/scoped_feature_list.h" #include "base/test/simple_test_clock.h" #include "base/test/simple_test_tick_clock.h" #include "components/client_update_protocol/ecdsa.h" #include "components/network_time/network_time_pref_names.h" +#include "components/network_time/network_time_test_utils.h" #include "components/prefs/testing_pref_service.h" -#include "components/variations/variations_associated_data.h" #include "net/http/http_response_headers.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/http_response.h" @@ -45,7 +39,7 @@ "NetworkTimeTracker.WallClockRanBackwards"; } // namespace -class NetworkTimeTrackerTest : public testing::Test { +class NetworkTimeTrackerTest : public FieldTrialTest { public: ~NetworkTimeTrackerTest() override {} @@ -180,50 +174,6 @@ } protected: - void SetNetworkQueriesWithVariationsService(bool enable, - float query_probability) { - const std::string kTrialName = "Trial"; - const std::string kGroupName = "group"; - const base::Feature kFeature{"NetworkTimeServiceQuerying", - base::FEATURE_DISABLED_BY_DEFAULT}; - - // Clear all the things. - variations::testing::ClearAllVariationParams(); - - std::map<std::string, std::string> params; - params["RandomQueryProbability"] = base::DoubleToString(query_probability); - params["CheckTimeIntervalSeconds"] = base::Int64ToString(360); - - // There are 3 things here: a FieldTrial, a FieldTrialList, and a - // FeatureList. Don't get confused! The FieldTrial is reference-counted, - // and a reference is held by the FieldTrialList. The FieldTrialList and - // FeatureList are both singletons. The authorized way to reset the former - // for testing is to destruct it (above). The latter, by contrast, should - // should already start in a clean state and can be manipulated via the - // ScopedFeatureList helper class. If this comment was useful to you - // please send me a postcard. - - field_trial_list_.reset(); // Averts a CHECK fail in constructor below. - field_trial_list_.reset( - new base::FieldTrialList( - base::MakeUnique<base::MockEntropyProvider>())); - // refcounted, and reference held by field_trial_list_. - base::FieldTrial* trial = base::FieldTrialList::FactoryGetFieldTrial( - kTrialName, 100, kGroupName, 1971, 1, 1, - base::FieldTrial::SESSION_RANDOMIZED, - nullptr /* default_group_number */); - ASSERT_TRUE( - variations::AssociateVariationParams(kTrialName, kGroupName, params)); - - std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); - feature_list->RegisterFieldTrialOverride( - kFeature.name, enable ? base::FeatureList::OVERRIDE_ENABLE_FEATURE - : base::FeatureList::OVERRIDE_DISABLE_FEATURE, - trial); - scoped_feature_list_.reset(new base::test::ScopedFeatureList); - scoped_feature_list_->InitWithFeatureList(std::move(feature_list)); - } - base::Thread io_thread_; base::MessageLoop message_loop_; base::TimeDelta resolution_; @@ -232,10 +182,8 @@ base::SimpleTestClock* clock_; base::SimpleTestTickClock* tick_clock_; TestingPrefServiceSimple pref_service_; - std::unique_ptr<base::FieldTrialList> field_trial_list_; std::unique_ptr<NetworkTimeTracker> tracker_; std::unique_ptr<net::EmbeddedTestServer> test_server_; - std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_; }; TEST_F(NetworkTimeTrackerTest, Uninitialized) {
diff --git a/components/offline_pages/background/offliner_policy.h b/components/offline_pages/background/offliner_policy.h index a82f5e68..d3424f39 100644 --- a/components/offline_pages/background/offliner_policy.h +++ b/components/offline_pages/background/offliner_policy.h
@@ -8,7 +8,7 @@ namespace { const int kMaxStartedTries = 4; const int kMaxCompletedTries = 1; -const int kBackgroundProcessingTimeBudgetSeconds = 170; +const int kDefaultBackgroundProcessingTimeBudgetSeconds = 170; const int kSinglePageTimeLimitWhenBackgroundScheduledSeconds = 120; const int kSinglePageTimeLimitForImmediateLoadSeconds = 300; const int kRequestExpirationTimeInSeconds = 60 * 60 * 24 * 7; @@ -25,19 +25,23 @@ prefer_earlier_requests_(true), retry_count_is_more_important_than_recency_(false), max_started_tries_(kMaxStartedTries), - max_completed_tries_(kMaxCompletedTries) {} + max_completed_tries_(kMaxCompletedTries), + background_processing_time_budget_( + kDefaultBackgroundProcessingTimeBudgetSeconds) {} // Constructor for unit tests. OfflinerPolicy(bool prefer_untried, bool prefer_earlier, bool prefer_retry_count, int max_started_tries, - int max_completed_tries) + int max_completed_tries, + int background_processing_time_budget) : prefer_untried_requests_(prefer_untried), prefer_earlier_requests_(prefer_earlier), retry_count_is_more_important_than_recency_(prefer_retry_count), max_started_tries_(max_started_tries), - max_completed_tries_(max_completed_tries) {} + max_completed_tries_(max_completed_tries), + background_processing_time_budget_(background_processing_time_budget) {} // TODO(petewil): Numbers here are chosen arbitrarily, do the proper studies // to get good policy numbers. Eventually this should get data from a finch @@ -84,7 +88,7 @@ // How many seconds to keep trying new pages for, before we give up, and // return to the scheduler. int GetBackgroundProcessingTimeBudgetSeconds() const { - return kBackgroundProcessingTimeBudgetSeconds; + return background_processing_time_budget_; } // How long do we allow a page to load before giving up on it when @@ -110,6 +114,7 @@ bool retry_count_is_more_important_than_recency_; int max_started_tries_; int max_completed_tries_; + int background_processing_time_budget_; }; } // namespace offline_pages
diff --git a/components/offline_pages/background/request_coordinator.cc b/components/offline_pages/background/request_coordinator.cc index 7d4eebf..81d90f1 100644 --- a/components/offline_pages/background/request_coordinator.cc +++ b/components/offline_pages/background/request_coordinator.cc
@@ -136,6 +136,7 @@ network_quality_estimator_(network_quality_estimator), active_request_(nullptr), last_offlining_status_(Offliner::RequestStatus::UNKNOWN), + immediate_schedule_callback_(base::Bind(&EmptySchedulerCallback)), weak_ptr_factory_(this) { DCHECK(policy_ != nullptr); picker_.reset( @@ -464,10 +465,10 @@ // Start processing with manufactured conservative battery conditions // (i.e., assume no battery). // TODO(dougarnett): Obtain actual battery conditions (from Android/Java). + DeviceConditions device_conditions(false, 0, GetConnectionType()); if (StartProcessingInternal(ProcessingWindowState::IMMEDIATE_WINDOW, - device_conditions, - base::Bind(&EmptySchedulerCallback))) + device_conditions, immediate_schedule_callback_)) return OfflinerImmediateStartStatus::STARTED; else return OfflinerImmediateStartStatus::NOT_ACCEPTED; @@ -646,6 +647,8 @@ case Offliner::RequestStatus::PRERENDERING_CANCELED: case Offliner::RequestStatus::PRERENDERING_FAILED: // No further processing in this service window. + // Let the scheduler know we are done processing. + scheduler_callback_.Run(true); break; default: // Make explicit choice about new status codes that actually reach here.
diff --git a/components/offline_pages/background/request_coordinator.h b/components/offline_pages/background/request_coordinator.h index c523edb..371fd23 100644 --- a/components/offline_pages/background/request_coordinator.h +++ b/components/offline_pages/background/request_coordinator.h
@@ -137,6 +137,14 @@ scheduler_callback_ = callback; } + // A way to set the callback which would be called if the request will be + // scheduled immediately. Used by testing harness to determine if a request + // has been processed. + void SetImmediateScheduleCallbackForTest( + const base::Callback<void(bool)> callback) { + immediate_schedule_callback_ = callback; + } + // Observers implementing the RequestCoordinator::Observer interface can // register here to get notifications of changes to request state. This // pointer is not owned, and it is the callers responsibility to remove the @@ -367,6 +375,8 @@ RequestCoordinatorEventLogger event_logger_; // Timer to watch for pre-render attempts running too long. base::OneShotTimer watchdog_timer_; + // Callback invoked when an immediate request is done (default empty). + base::Callback<void(bool)> immediate_schedule_callback_; // Allows us to pass a weak pointer to callbacks. base::WeakPtrFactory<RequestCoordinator> weak_ptr_factory_;
diff --git a/components/offline_pages/background/request_coordinator_unittest.cc b/components/offline_pages/background/request_coordinator_unittest.cc index 6a2c95d..52d442c 100644 --- a/components/offline_pages/background/request_coordinator_unittest.cc +++ b/components/offline_pages/background/request_coordinator_unittest.cc
@@ -68,7 +68,6 @@ conditions_ = trigger_conditions; } - // Unschedules the currently scheduled task, if any. void Unschedule() override { unschedule_called_ = true; @@ -239,7 +238,8 @@ bool is_starting() { return coordinator_->is_starting(); } // Empty callback function. - void EmptyCallbackFunction(bool result) { + void ImmediateScheduleCallbackFunction(bool result) { + immediate_schedule_callback_called_ = true; } // Callback function which releases a wait for it. @@ -326,6 +326,10 @@ ObserverStub observer() { return observer_; } + bool immediate_schedule_callback_called() const { + return immediate_schedule_callback_called_; + } + private: RequestQueue::GetRequestsResult last_get_requests_result_; MultipleItemStatuses last_remove_results_; @@ -337,6 +341,7 @@ OfflinerStub* offliner_; base::WaitableEvent waiter_; ObserverStub observer_; + bool immediate_schedule_callback_called_; }; RequestCoordinatorTest::RequestCoordinatorTest() @@ -345,7 +350,8 @@ task_runner_handle_(task_runner_), offliner_(nullptr), waiter_(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED) {} + base::WaitableEvent::InitialState::NOT_SIGNALED), + immediate_schedule_callback_called_(false) {} RequestCoordinatorTest::~RequestCoordinatorTest() {} @@ -407,10 +413,11 @@ DeviceConditions device_conditions(false, 75, net::NetworkChangeNotifier::CONNECTION_3G); base::Callback<void(bool)> callback = - base::Bind( - &RequestCoordinatorTest::EmptyCallbackFunction, - base::Unretained(this)); + base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, + base::Unretained(this)); EXPECT_TRUE(coordinator()->StartProcessing(device_conditions, callback)); + PumpLoop(); + EXPECT_TRUE(immediate_schedule_callback_called()); } TEST_F(RequestCoordinatorTest, StartProcessingWithRequestInProgress) { @@ -424,7 +431,7 @@ DeviceConditions device_conditions( false, 75, net::NetworkChangeNotifier::CONNECTION_3G); base::Callback<void(bool)> callback = - base::Bind(&RequestCoordinatorTest::EmptyCallbackFunction, + base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, base::Unretained(this)); // Ensure that the forthcoming request does not finish - we simulate it being @@ -435,24 +442,88 @@ EXPECT_TRUE(coordinator()->StartProcessing(device_conditions, callback)); PumpLoop(); EXPECT_TRUE(is_busy()); + // Since the offliner is disabled, this callback should not be called. + EXPECT_FALSE(immediate_schedule_callback_called()); // Now trying to start processing on another request should return false. EXPECT_FALSE(coordinator()->StartProcessing(device_conditions, callback)); } TEST_F(RequestCoordinatorTest, SavePageLater) { + // Set up device conditions for the test and enable the offliner. + DeviceConditions device_conditions(false, 75, + net::NetworkChangeNotifier::CONNECTION_3G); + SetDeviceConditionsForTest(device_conditions); + SetEffectiveConnectionTypeForTest( + net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_3G); + EnableOfflinerCallback(true); + base::Callback<void(bool)> callback = + base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, + base::Unretained(this)); + + // The user-requested request which gets processed by SavePageLater + // would invoke user request callback. + coordinator()->SetImmediateScheduleCallbackForTest(callback); + EXPECT_NE( coordinator()->SavePageLater( kUrl1, kClientId1, kUserRequested, RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER), 0); // Expect that a request got placed on the queue. + coordinator()->queue()->GetRequests(base::Bind( + &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this))); + + // Wait for callbacks to finish, both request queue and offliner. + PumpLoop(); + EXPECT_TRUE(immediate_schedule_callback_called()); + + // Check the request queue is as expected. + EXPECT_EQ(1UL, last_requests().size()); + EXPECT_EQ(kUrl1, last_requests().at(0)->url()); + EXPECT_EQ(kClientId1, last_requests().at(0)->client_id()); + + // Expect that the scheduler got notified. + SchedulerStub* scheduler_stub = + reinterpret_cast<SchedulerStub*>(coordinator()->scheduler()); + EXPECT_TRUE(scheduler_stub->schedule_called()); + EXPECT_EQ(coordinator() + ->GetTriggerConditions(last_requests()[0]->user_requested()) + .minimum_battery_percentage, + scheduler_stub->conditions()->minimum_battery_percentage); + + // Check that the observer got the notification that a page is available + EXPECT_TRUE(observer().added_called()); +} + +TEST_F(RequestCoordinatorTest, SavePageLaterFailed) { + // Set up device conditions for the test and enable the offliner. + DeviceConditions device_conditions(false, 75, + net::NetworkChangeNotifier::CONNECTION_3G); + SetDeviceConditionsForTest(device_conditions); + SetEffectiveConnectionTypeForTest( + net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_3G); + base::Callback<void(bool)> callback = + base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, + base::Unretained(this)); + // The user-requested request which gets processed by SavePageLater + // would invoke user request callback. + coordinator()->SetImmediateScheduleCallbackForTest(callback); + + EXPECT_TRUE( + coordinator()->SavePageLater( + kUrl1, kClientId1, kUserRequested, + RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER) != 0); + + // Expect that a request got placed on the queue. coordinator()->queue()->GetRequests( base::Bind(&RequestCoordinatorTest::GetRequestsDone, base::Unretained(this))); // Wait for callbacks to finish, both request queue and offliner. PumpLoop(); + // Will not be called since the offliner is disabled. + EXPECT_FALSE(immediate_schedule_callback_called()); // Check the request queue is as expected. EXPECT_EQ(1UL, last_requests().size()); @@ -485,9 +556,8 @@ // We need to give a callback to the request. base::Callback<void(bool)> callback = - base::Bind( - &RequestCoordinatorTest::EmptyCallbackFunction, - base::Unretained(this)); + base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, + base::Unretained(this)); coordinator()->SetProcessingCallbackForTest(callback); // Set up device conditions for the test. @@ -499,6 +569,7 @@ // for callbacks. SendOfflinerDoneCallback(request, Offliner::RequestStatus::SAVED); PumpLoop(); + EXPECT_TRUE(immediate_schedule_callback_called()); // Verify the request gets removed from the queue, and wait for callbacks. coordinator()->queue()->GetRequests( @@ -539,9 +610,8 @@ // We need to give a callback to the request. base::Callback<void(bool)> callback = - base::Bind( - &RequestCoordinatorTest::EmptyCallbackFunction, - base::Unretained(this)); + base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, + base::Unretained(this)); coordinator()->SetProcessingCallbackForTest(callback); // Set up device conditions for the test. @@ -554,6 +624,7 @@ SendOfflinerDoneCallback(request, Offliner::RequestStatus::PRERENDERING_FAILED); PumpLoop(); + EXPECT_TRUE(immediate_schedule_callback_called()); // TODO(dougarnett): Consider injecting mock RequestPicker for this test // and verifying that there is no attempt to pick another request following @@ -593,8 +664,9 @@ PumpLoop(); // We need to give a callback to the request. - base::Callback<void(bool)> callback = base::Bind( - &RequestCoordinatorTest::EmptyCallbackFunction, base::Unretained(this)); + base::Callback<void(bool)> callback = + base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, + base::Unretained(this)); coordinator()->SetProcessingCallbackForTest(callback); // Set up device conditions for the test. @@ -607,6 +679,7 @@ SendOfflinerDoneCallback( request, Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY); PumpLoop(); + EXPECT_TRUE(immediate_schedule_callback_called()); // TODO(dougarnett): Consider injecting mock RequestPicker for this test // and verifying that there is as attempt to pick another request following @@ -636,8 +709,9 @@ PumpLoop(); // We need to give a callback to the request. - base::Callback<void(bool)> callback = base::Bind( - &RequestCoordinatorTest::EmptyCallbackFunction, base::Unretained(this)); + base::Callback<void(bool)> callback = + base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, + base::Unretained(this)); coordinator()->SetProcessingCallbackForTest(callback); // Set up device conditions for the test. @@ -650,6 +724,7 @@ SendOfflinerDoneCallback(request, Offliner::RequestStatus::FOREGROUND_CANCELED); PumpLoop(); + EXPECT_TRUE(immediate_schedule_callback_called()); // Verify the request is not removed from the queue, and wait for callbacks. coordinator()->queue()->GetRequests(base::Bind( @@ -673,8 +748,9 @@ PumpLoop(); // We need to give a callback to the request. - base::Callback<void(bool)> callback = base::Bind( - &RequestCoordinatorTest::EmptyCallbackFunction, base::Unretained(this)); + base::Callback<void(bool)> callback = + base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, + base::Unretained(this)); coordinator()->SetProcessingCallbackForTest(callback); // Set up device conditions for the test. @@ -687,6 +763,7 @@ SendOfflinerDoneCallback(request, Offliner::RequestStatus::PRERENDERING_CANCELED); PumpLoop(); + EXPECT_TRUE(immediate_schedule_callback_called()); // Verify the request is not removed from the queue, and wait for callbacks. coordinator()->queue()->GetRequests(base::Bind( @@ -707,8 +784,9 @@ // Call start processing just to set up a scheduler callback. DeviceConditions device_conditions(false, 75, net::NetworkChangeNotifier::CONNECTION_3G); - base::Callback<void(bool)> callback = base::Bind( - &RequestCoordinatorTest::EmptyCallbackFunction, base::Unretained(this)); + base::Callback<void(bool)> callback = + base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, + base::Unretained(this)); coordinator()->StartProcessing(device_conditions, callback); EXPECT_TRUE(is_starting()); @@ -732,8 +810,9 @@ // Call start processing just to set up a scheduler callback. DeviceConditions device_conditions(false, 75, net::NetworkChangeNotifier::CONNECTION_3G); - base::Callback<void(bool)> callback = base::Bind( - &RequestCoordinatorTest::EmptyCallbackFunction, base::Unretained(this)); + base::Callback<void(bool)> callback = + base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, + base::Unretained(this)); coordinator()->StartProcessing(device_conditions, callback); EXPECT_TRUE(is_starting()); @@ -743,6 +822,7 @@ PumpLoop(); EXPECT_FALSE(is_starting()); + EXPECT_TRUE(immediate_schedule_callback_called()); // The scheduler should have been called to schedule the non-user requested // task. @@ -807,13 +887,13 @@ net::NetworkChangeNotifier::CONNECTION_3G); DisableLoading(); base::Callback<void(bool)> callback = - base::Bind( - &RequestCoordinatorTest::EmptyCallbackFunction, - base::Unretained(this)); + base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, + base::Unretained(this)); EXPECT_TRUE(coordinator()->StartProcessing(device_conditions, callback)); // Let the async callbacks in the request coordinator run. PumpLoop(); + EXPECT_TRUE(immediate_schedule_callback_called()); EXPECT_FALSE(is_starting()); EXPECT_EQ(Offliner::PRERENDERING_NOT_STARTED, last_offlining_status()); @@ -834,9 +914,8 @@ DeviceConditions device_conditions(false, 75, net::NetworkChangeNotifier::CONNECTION_3G); base::Callback<void(bool)> callback = - base::Bind( - &RequestCoordinatorTest::EmptyCallbackFunction, - base::Unretained(this)); + base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, + base::Unretained(this)); EXPECT_TRUE(coordinator()->StartProcessing(device_conditions, callback)); EXPECT_TRUE(is_starting()); @@ -845,6 +924,7 @@ // Let the async callbacks in the request coordinator run. PumpLoop(); + EXPECT_TRUE(immediate_schedule_callback_called()); EXPECT_FALSE(is_starting()); @@ -874,14 +954,15 @@ DeviceConditions device_conditions(false, 75, net::NetworkChangeNotifier::CONNECTION_3G); base::Callback<void(bool)> callback = - base::Bind( - &RequestCoordinatorTest::EmptyCallbackFunction, - base::Unretained(this)); + base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, + base::Unretained(this)); EXPECT_TRUE(coordinator()->StartProcessing(device_conditions, callback)); EXPECT_TRUE(is_starting()); // Let all the async parts of the start processing pipeline run to completion. PumpLoop(); + // Since the offliner is disabled, this callback should not be called. + EXPECT_FALSE(immediate_schedule_callback_called()); // Coordinator should now be busy. EXPECT_TRUE(is_busy()); @@ -920,12 +1001,15 @@ DeviceConditions device_conditions(false, 75, net::NetworkChangeNotifier::CONNECTION_3G); - base::Callback<void(bool)> callback = base::Bind( - &RequestCoordinatorTest::EmptyCallbackFunction, base::Unretained(this)); + base::Callback<void(bool)> callback = + base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, + base::Unretained(this)); EXPECT_TRUE(coordinator()->StartProcessing(device_conditions, callback)); // Let all the async parts of the start processing pipeline run to completion. PumpLoop(); + // Since the offliner is disabled, this callback should not be called. + EXPECT_FALSE(immediate_schedule_callback_called()); // Remove the request while it is processing. std::vector<int64_t> request_ids{kRequestId1}; @@ -955,13 +1039,15 @@ DeviceConditions device_conditions(false, 75, net::NetworkChangeNotifier::CONNECTION_3G); - base::Callback<void(bool)> callback = base::Bind( - &RequestCoordinatorTest::EmptyCallbackFunction, base::Unretained(this)); + base::Callback<void(bool)> callback = + base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, + base::Unretained(this)); EXPECT_TRUE(coordinator()->StartProcessing(device_conditions, callback)); // Call the method under test, making sure we send SUCCESS to the observer. coordinator()->MarkRequestCompleted(request_id); PumpLoop(); + EXPECT_TRUE(immediate_schedule_callback_called()); // Our observer should have seen SUCCESS instead of REMOVED. EXPECT_EQ(RequestCoordinator::BackgroundSavePageResult::SUCCESS,
diff --git a/components/offline_pages/background/request_picker_unittest.cc b/components/offline_pages/background/request_picker_unittest.cc index f71b4064..963f9aa 100644 --- a/components/offline_pages/background/request_picker_unittest.cc +++ b/components/offline_pages/background/request_picker_unittest.cc
@@ -38,6 +38,7 @@ const bool kPreferUntried = false; const bool kPreferEarlier = true; const bool kPreferRetryCount = true; +const int kBackgroundProcessingTimeBudgetSeconds = 170; // Default request const SavePageRequest kEmptyRequest(0UL, @@ -194,9 +195,9 @@ } TEST_F(RequestPickerTest, ChooseRequestWithHigherRetryCount) { - policy_.reset(new OfflinerPolicy(kPreferUntried, kPreferEarlier, - kPreferRetryCount, kMaxStartedTries, - kMaxCompletedTries + 1)); + policy_.reset(new OfflinerPolicy( + kPreferUntried, kPreferEarlier, kPreferRetryCount, kMaxStartedTries, + kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds)); picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(), &event_logger_)); @@ -230,9 +231,9 @@ TEST_F(RequestPickerTest, ChooseEarlierRequest) { // We need a custom policy object prefering recency to retry count. - policy_.reset(new OfflinerPolicy(kPreferUntried, kPreferEarlier, - !kPreferRetryCount, kMaxStartedTries, - kMaxCompletedTries)); + policy_.reset(new OfflinerPolicy( + kPreferUntried, kPreferEarlier, !kPreferRetryCount, kMaxStartedTries, + kMaxCompletedTries, kBackgroundProcessingTimeBudgetSeconds)); picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(), &event_logger_)); @@ -253,9 +254,9 @@ TEST_F(RequestPickerTest, ChooseSameTimeRequestWithHigherRetryCount) { // We need a custom policy object preferring recency to retry count. - policy_.reset(new OfflinerPolicy(kPreferUntried, kPreferEarlier, - !kPreferRetryCount, kMaxStartedTries, - kMaxCompletedTries + 1)); + policy_.reset(new OfflinerPolicy( + kPreferUntried, kPreferEarlier, !kPreferRetryCount, kMaxStartedTries, + kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds)); picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(), &event_logger_)); @@ -274,9 +275,9 @@ TEST_F(RequestPickerTest, ChooseRequestWithLowerRetryCount) { // We need a custom policy object preferring lower retry count. - policy_.reset(new OfflinerPolicy(!kPreferUntried, kPreferEarlier, - kPreferRetryCount, kMaxStartedTries, - kMaxCompletedTries + 1)); + policy_.reset(new OfflinerPolicy( + !kPreferUntried, kPreferEarlier, kPreferRetryCount, kMaxStartedTries, + kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds)); picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(), &event_logger_)); @@ -295,9 +296,9 @@ TEST_F(RequestPickerTest, ChooseLaterRequest) { // We need a custom policy preferring recency over retry, and later requests. - policy_.reset(new OfflinerPolicy(kPreferUntried, !kPreferEarlier, - !kPreferRetryCount, kMaxStartedTries, - kMaxCompletedTries)); + policy_.reset(new OfflinerPolicy( + kPreferUntried, !kPreferEarlier, !kPreferRetryCount, kMaxStartedTries, + kMaxCompletedTries, kBackgroundProcessingTimeBudgetSeconds)); picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(), &event_logger_)); @@ -377,9 +378,9 @@ TEST_F(RequestPickerTest, ChooseRequestThatIsNotDisabled) { - policy_.reset(new OfflinerPolicy(kPreferUntried, kPreferEarlier, - kPreferRetryCount, kMaxStartedTries, - kMaxCompletedTries + 1)); + policy_.reset(new OfflinerPolicy( + kPreferUntried, kPreferEarlier, kPreferRetryCount, kMaxStartedTries, + kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds)); picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(), &event_logger_));
diff --git a/components/proximity_auth/connection.cc b/components/proximity_auth/connection.cc index 5b61e06..3c7ff25 100644 --- a/components/proximity_auth/connection.cc +++ b/components/proximity_auth/connection.cc
@@ -60,8 +60,8 @@ Status old_status = status_; status_ = status; - FOR_EACH_OBSERVER(ConnectionObserver, observers_, - OnConnectionStatusChanged(this, old_status, status_)); + for (auto& observer : observers_) + observer.OnConnectionStatusChanged(this, old_status, status_); } void Connection::OnDidSendMessage(const WireMessage& message, bool success) { @@ -71,8 +71,8 @@ } is_sending_message_ = false; - FOR_EACH_OBSERVER( - ConnectionObserver, observers_, OnSendCompleted(*this, message, success)); + for (auto& observer : observers_) + observer.OnSendCompleted(*this, message, success); } void Connection::OnBytesReceived(const std::string& bytes) { @@ -90,8 +90,8 @@ return; if (message) { - FOR_EACH_OBSERVER( - ConnectionObserver, observers_, OnMessageReceived(*this, *message)); + for (auto& observer : observers_) + observer.OnMessageReceived(*this, *message); } // Whether the message was parsed successfully or not, clear the
diff --git a/components/proximity_auth/cryptauth/cryptauth_device_manager.cc b/components/proximity_auth/cryptauth/cryptauth_device_manager.cc index c74d523..2d288c0 100644 --- a/components/proximity_auth/cryptauth/cryptauth_device_manager.cc +++ b/components/proximity_auth/cryptauth/cryptauth_device_manager.cc
@@ -215,11 +215,12 @@ sync_request_->OnDidComplete(true); cryptauth_client_.reset(); sync_request_.reset(); - FOR_EACH_OBSERVER( - Observer, observers_, - OnSyncFinished(SyncResult::SUCCESS, unlock_keys_changed - ? DeviceChangeResult::CHANGED - : DeviceChangeResult::UNCHANGED)); + for (auto& observer : observers_) { + observer.OnSyncFinished(SyncResult::SUCCESS, + unlock_keys_changed + ? DeviceChangeResult::CHANGED + : DeviceChangeResult::UNCHANGED); + } } void CryptAuthDeviceManager::OnGetMyDevicesFailure(const std::string& error) { @@ -229,9 +230,8 @@ sync_request_->OnDidComplete(false); cryptauth_client_.reset(); sync_request_.reset(); - FOR_EACH_OBSERVER( - Observer, observers_, - OnSyncFinished(SyncResult::FAILURE, DeviceChangeResult::UNCHANGED)); + for (auto& observer : observers_) + observer.OnSyncFinished(SyncResult::FAILURE, DeviceChangeResult::UNCHANGED); } std::unique_ptr<SyncScheduler> CryptAuthDeviceManager::CreateSyncScheduler() { @@ -268,7 +268,8 @@ void CryptAuthDeviceManager::OnSyncRequested( std::unique_ptr<SyncScheduler::SyncRequest> sync_request) { - FOR_EACH_OBSERVER(Observer, observers_, OnSyncStarted()); + for (auto& observer : observers_) + observer.OnSyncStarted(); sync_request_ = std::move(sync_request); cryptauth_client_ = client_factory_->CreateInstance();
diff --git a/components/proximity_auth/cryptauth/cryptauth_enrollment_manager.cc b/components/proximity_auth/cryptauth/cryptauth_enrollment_manager.cc index c2f8093c..ca65b47 100644 --- a/components/proximity_auth/cryptauth/cryptauth_enrollment_manager.cc +++ b/components/proximity_auth/cryptauth/cryptauth_enrollment_manager.cc
@@ -155,7 +155,8 @@ sync_request_->OnDidComplete(success); cryptauth_enroller_.reset(); sync_request_.reset(); - FOR_EACH_OBSERVER(Observer, observers_, OnEnrollmentFinished(success)); + for (auto& observer : observers_) + observer.OnEnrollmentFinished(success); } std::unique_ptr<SyncScheduler> @@ -230,7 +231,8 @@ void CryptAuthEnrollmentManager::OnSyncRequested( std::unique_ptr<SyncScheduler::SyncRequest> sync_request) { - FOR_EACH_OBSERVER(Observer, observers_, OnEnrollmentStarted()); + for (auto& observer : observers_) + observer.OnEnrollmentStarted(); sync_request_ = std::move(sync_request); if (gcm_manager_->GetRegistrationId().empty() ||
diff --git a/components/proximity_auth/cryptauth/cryptauth_gcm_manager_impl.cc b/components/proximity_auth/cryptauth/cryptauth_gcm_manager_impl.cc index 588ddcf..77680e8f 100644 --- a/components/proximity_auth/cryptauth/cryptauth_gcm_manager_impl.cc +++ b/components/proximity_auth/cryptauth/cryptauth_gcm_manager_impl.cc
@@ -102,9 +102,11 @@ if (tickle_type == kRegistrationTickleTypeForceEnrollment || tickle_type == kRegistrationTickleTypeUpdateEnrollment) { // These tickle types correspond to re-enrollment messages. - FOR_EACH_OBSERVER(Observer, observers_, OnReenrollMessage()); + for (auto& observer : observers_) + observer.OnReenrollMessage(); } else if (tickle_type == kRegistrationTickleTypeDevicesSync) { - FOR_EACH_OBSERVER(Observer, observers_, OnResyncMessage()); + for (auto& observer : observers_) + observer.OnResyncMessage(); } else { PA_LOG(WARNING) << "Unknown tickle type in GCM message."; } @@ -133,14 +135,16 @@ if (result != gcm::GCMClient::SUCCESS) { PA_LOG(WARNING) << "GCM registration failed with result=" << static_cast<int>(result); - FOR_EACH_OBSERVER(Observer, observers_, OnGCMRegistrationResult(false)); + for (auto& observer : observers_) + observer.OnGCMRegistrationResult(false); return; } PA_LOG(INFO) << "GCM registration success, registration_id=" << registration_id; pref_service_->SetString(prefs::kCryptAuthGCMRegistrationId, registration_id); - FOR_EACH_OBSERVER(Observer, observers_, OnGCMRegistrationResult(true)); + for (auto& observer : observers_) + observer.OnGCMRegistrationResult(true); } } // namespace proximity_auth
diff --git a/components/proximity_auth/cryptauth/fake_cryptauth_gcm_manager.cc b/components/proximity_auth/cryptauth/fake_cryptauth_gcm_manager.cc index 806e1be..d1e85c6 100644 --- a/components/proximity_auth/cryptauth/fake_cryptauth_gcm_manager.cc +++ b/components/proximity_auth/cryptauth/fake_cryptauth_gcm_manager.cc
@@ -36,15 +36,18 @@ registration_in_progress_ = false; registration_id_ = registration_id; bool success = !registration_id_.empty(); - FOR_EACH_OBSERVER(Observer, observers_, OnGCMRegistrationResult(success)); + for (auto& observer : observers_) + observer.OnGCMRegistrationResult(success); } void FakeCryptAuthGCMManager::PushReenrollMessage() { - FOR_EACH_OBSERVER(Observer, observers_, OnReenrollMessage()); + for (auto& observer : observers_) + observer.OnReenrollMessage(); } void FakeCryptAuthGCMManager::PushResyncMessage() { - FOR_EACH_OBSERVER(Observer, observers_, OnResyncMessage()); + for (auto& observer : observers_) + observer.OnResyncMessage(); } } // namespace proximity_auth
diff --git a/components/proximity_auth/cryptauth/mock_cryptauth_client.cc b/components/proximity_auth/cryptauth/mock_cryptauth_client.cc index 51dd4eb8..4480623 100644 --- a/components/proximity_auth/cryptauth/mock_cryptauth_client.cc +++ b/components/proximity_auth/cryptauth/mock_cryptauth_client.cc
@@ -30,8 +30,8 @@ else client.reset(new testing::NiceMock<MockCryptAuthClient>()); - FOR_EACH_OBSERVER(Observer, observer_list_, - OnCryptAuthClientCreated(client.get())); + for (auto& observer : observer_list_) + observer.OnCryptAuthClientCreated(client.get()); return std::move(client); }
diff --git a/components/proximity_auth/logging/log_buffer.cc b/components/proximity_auth/logging/log_buffer.cc index 5e23d220..8f53153 100644 --- a/components/proximity_auth/logging/log_buffer.cc +++ b/components/proximity_auth/logging/log_buffer.cc
@@ -51,12 +51,14 @@ log_messages_.push_back(log_message); if (log_messages_.size() > MaxBufferSize()) log_messages_.pop_front(); - FOR_EACH_OBSERVER(Observer, observers_, OnLogMessageAdded(log_message)); + for (auto& observer : observers_) + observer.OnLogMessageAdded(log_message); } void LogBuffer::Clear() { log_messages_.clear(); - FOR_EACH_OBSERVER(Observer, observers_, OnLogBufferCleared()); + for (auto& observer : observers_) + observer.OnLogBufferCleared(); } size_t LogBuffer::MaxBufferSize() const {
diff --git a/components/proximity_auth/messenger_impl.cc b/components/proximity_auth/messenger_impl.cc index 1a0d027..05169bf 100644 --- a/components/proximity_auth/messenger_impl.cc +++ b/components/proximity_auth/messenger_impl.cc
@@ -115,8 +115,8 @@ if (!SupportsSignIn()) { PA_LOG(WARNING) << "Dropping decryption request, as remote device " << "does not support protocol v3.1."; - FOR_EACH_OBSERVER(MessengerObserver, observers_, - OnDecryptResponse(std::string())); + for (auto& observer : observers_) + observer.OnDecryptResponse(std::string()); return; } @@ -137,7 +137,8 @@ if (!SupportsSignIn()) { PA_LOG(WARNING) << "Dropping unlock request, as remote device does not " << "support protocol v3.1."; - FOR_EACH_OBSERVER(MessengerObserver, observers_, OnUnlockResponse(false)); + for (auto& observer : observers_) + observer.OnUnlockResponse(false); return; } @@ -189,8 +190,8 @@ (decoded_message == kScreenUnlocked ? USER_PRESENT : USER_ABSENT); update.secure_screen_lock_state = SECURE_SCREEN_LOCK_ENABLED; update.trust_agent_state = TRUST_AGENT_ENABLED; - FOR_EACH_OBSERVER(MessengerObserver, observers_, - OnRemoteStatusUpdate(update)); + for (auto& observer : observers_) + observer.OnRemoteStatusUpdate(update); pending_message_.reset(); ProcessMessageQueue(); return; @@ -263,8 +264,8 @@ return; } - FOR_EACH_OBSERVER(MessengerObserver, observers_, - OnRemoteStatusUpdate(*status_update)); + for (auto& observer : observers_) + observer.OnRemoteStatusUpdate(*status_update); } void MessengerImpl::HandleDecryptResponseMessage( @@ -279,13 +280,14 @@ PA_LOG(ERROR) << "Unable to base64-decode decrypt response."; } - FOR_EACH_OBSERVER(MessengerObserver, observers_, - OnDecryptResponse(decrypted_data)); + for (auto& observer : observers_) + observer.OnDecryptResponse(decrypted_data); } void MessengerImpl::HandleUnlockResponseMessage( const base::DictionaryValue& message) { - FOR_EACH_OBSERVER(MessengerObserver, observers_, OnUnlockResponse(true)); + for (auto& observer : observers_) + observer.OnUnlockResponse(true); } void MessengerImpl::PollScreenStateForIOS() { @@ -310,7 +312,8 @@ if (new_status == Connection::DISCONNECTED) { PA_LOG(INFO) << "Secure channel disconnected..."; connection_->RemoveObserver(this); - FOR_EACH_OBSERVER(MessengerObserver, observers_, OnDisconnected()); + for (auto& observer : observers_) + observer.OnDisconnected(); // TODO(isherman): Determine whether it's also necessary/appropriate to fire // this notification from the destructor. } @@ -342,13 +345,14 @@ // For local events, we don't expect a response, so on success, we // notify observers right away. if (pending_message_->type == kMessageTypeDecryptRequest) { - FOR_EACH_OBSERVER(MessengerObserver, observers_, - OnDecryptResponse(std::string())); + for (auto& observer : observers_) + observer.OnDecryptResponse(std::string()); } else if (pending_message_->type == kMessageTypeUnlockRequest) { - FOR_EACH_OBSERVER(MessengerObserver, observers_, OnUnlockResponse(false)); + for (auto& observer : observers_) + observer.OnUnlockResponse(false); } else if (pending_message_->type == kMessageTypeLocalEvent) { - FOR_EACH_OBSERVER(MessengerObserver, observers_, - OnUnlockEventSent(success)); + for (auto& observer : observers_) + observer.OnUnlockEventSent(success); } else { PA_LOG(ERROR) << "Message of unknown type '" << pending_message_->type << "' sent.";
diff --git a/components/proximity_auth/proximity_monitor_impl.cc b/components/proximity_auth/proximity_monitor_impl.cc index 793062b..cf908b4 100644 --- a/components/proximity_auth/proximity_monitor_impl.cc +++ b/components/proximity_auth/proximity_monitor_impl.cc
@@ -221,8 +221,8 @@ void ProximityMonitorImpl::ClearProximityState() { if (is_active_ && remote_device_is_in_proximity_) { - FOR_EACH_OBSERVER(ProximityMonitorObserver, observers_, - OnProximityStateChanged()); + for (auto& observer : observers_) + observer.OnProximityStateChanged(); } remote_device_is_in_proximity_ = false; @@ -273,8 +273,8 @@ PA_LOG(INFO) << "[Proximity] Updated proximity state: " << (is_now_in_proximity ? "proximate" : "distant"); remote_device_is_in_proximity_ = is_now_in_proximity; - FOR_EACH_OBSERVER(ProximityMonitorObserver, observers_, - OnProximityStateChanged()); + for (auto& observer : observers_) + observer.OnProximityStateChanged(); } }
diff --git a/components/proximity_auth/remote_device_life_cycle_impl.cc b/components/proximity_auth/remote_device_life_cycle_impl.cc index a5c37be..0095e59 100644 --- a/components/proximity_auth/remote_device_life_cycle_impl.cc +++ b/components/proximity_auth/remote_device_life_cycle_impl.cc
@@ -107,8 +107,8 @@ << " => " << static_cast<int>(new_state); RemoteDeviceLifeCycle::State old_state = state_; state_ = new_state; - FOR_EACH_OBSERVER(Observer, observers_, - OnLifeCycleStateChanged(old_state, new_state)); + for (auto& observer : observers_) + observer.OnLifeCycleStateChanged(old_state, new_state); } void RemoteDeviceLifeCycleImpl::FindConnection() {
diff --git a/components/proximity_auth/screenlock_bridge.cc b/components/proximity_auth/screenlock_bridge.cc index 3a941148..d3996b1 100644 --- a/components/proximity_auth/screenlock_bridge.cc +++ b/components/proximity_auth/screenlock_bridge.cc
@@ -134,10 +134,13 @@ focused_account_id_ = EmptyAccountId(); lock_handler_ = lock_handler; - if (lock_handler_) - FOR_EACH_OBSERVER(Observer, observers_, OnScreenDidLock(screen_type)); - else - FOR_EACH_OBSERVER(Observer, observers_, OnScreenDidUnlock(screen_type)); + if (lock_handler_) { + for (auto& observer : observers_) + observer.OnScreenDidLock(screen_type); + } else { + for (auto& observer : observers_) + observer.OnScreenDidUnlock(screen_type); + } } void ScreenlockBridge::SetFocusedUser(const AccountId& account_id) { @@ -145,7 +148,8 @@ return; PA_LOG(INFO) << "Focused user changed to " << account_id.Serialize(); focused_account_id_ = account_id; - FOR_EACH_OBSERVER(Observer, observers_, OnFocusedUserChanged(account_id)); + for (auto& observer : observers_) + observer.OnFocusedUserChanged(account_id); } bool ScreenlockBridge::IsLocked() const {
diff --git a/components/renderer_context_menu/render_view_context_menu_base.cc b/components/renderer_context_menu/render_view_context_menu_base.cc index b743f9e..63d8df50 100644 --- a/components/renderer_context_menu/render_view_context_menu_base.cc +++ b/components/renderer_context_menu/render_view_context_menu_base.cc
@@ -351,9 +351,8 @@ source_web_contents_->NotifyContextMenuClosed(params_.custom_context); if (!command_executed_) { - FOR_EACH_OBSERVER(RenderViewContextMenuObserver, - observers_, - OnMenuCancel()); + for (auto& observer : observers_) + observer.OnMenuCancel(); } }
diff --git a/components/safe_browsing_db/v4_database.cc b/components/safe_browsing_db/v4_database.cc index 30a3b0f5..4b43498 100644 --- a/components/safe_browsing_db/v4_database.cc +++ b/components/safe_browsing_db/v4_database.cc
@@ -142,7 +142,9 @@ DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(pending_store_updates_); if (new_store) { - (*store_map_)[identifier] = std::move(new_store); + (*store_map_)[identifier].swap(new_store); + // |new_store| now is the store that needs to be destroyed on task runner. + V4Store::Destroy(std::move(new_store)); } pending_store_updates_--;
diff --git a/components/safe_browsing_db/v4_database_unittest.cc b/components/safe_browsing_db/v4_database_unittest.cc index 057602a..a72e6a8 100644 --- a/components/safe_browsing_db/v4_database_unittest.cc +++ b/components/safe_browsing_db/v4_database_unittest.cc
@@ -187,6 +187,11 @@ verify_checksum_called_back_ = true; } + void WaitForTasksOnTaskRunner() { + task_runner_->RunPendingTasks(); + base::RunLoop().RunUntilIdle(); + } + scoped_refptr<base::TestSimpleTaskRunner> task_runner_; std::unique_ptr<V4Database> v4_database_; base::FilePath database_dirname_; @@ -213,9 +218,7 @@ V4Database::Create(task_runner_, database_dirname_, list_infos_, callback_db_ready_); created_but_not_called_back_ = true; - task_runner_->RunPendingTasks(); - - base::RunLoop().RunUntilIdle(); + WaitForTasksOnTaskRunner(); EXPECT_EQ(true, created_and_called_back_); } @@ -226,8 +229,7 @@ V4Database::Create(task_runner_, database_dirname_, list_infos_, callback_db_ready_); created_but_not_called_back_ = true; - task_runner_->RunPendingTasks(); - base::RunLoop().RunUntilIdle(); + WaitForTasksOnTaskRunner(); // The database has now been created. Time to try to update it. EXPECT_TRUE(v4_database_); @@ -243,10 +245,13 @@ CreateFakeServerResponse(expected_store_state_map_, true), callback_db_updated_); - task_runner_->RunPendingTasks(); - base::RunLoop().RunUntilIdle(); + // Wait for the ApplyUpdate callback to get called. + WaitForTasksOnTaskRunner(); VerifyExpectedStoresState(true); + + // Wait for the old stores to get destroyed on task runner. + WaitForTasksOnTaskRunner(); } // Test to ensure no state updates leads to no store updates. @@ -256,8 +261,7 @@ V4Database::Create(task_runner_, database_dirname_, list_infos_, callback_db_ready_); created_but_not_called_back_ = true; - task_runner_->RunPendingTasks(); - base::RunLoop().RunUntilIdle(); + WaitForTasksOnTaskRunner(); // The database has now been created. Time to try to update it. EXPECT_TRUE(v4_database_); @@ -273,8 +277,7 @@ CreateFakeServerResponse(expected_store_state_map_, true), callback_db_updated_); - task_runner_->RunPendingTasks(); - base::RunLoop().RunUntilIdle(); + WaitForTasksOnTaskRunner(); VerifyExpectedStoresState(false); } @@ -286,8 +289,7 @@ V4Database::Create(task_runner_, database_dirname_, list_infos_, callback_db_ready_); created_but_not_called_back_ = true; - task_runner_->RunPendingTasks(); - base::RunLoop().RunUntilIdle(); + WaitForTasksOnTaskRunner(); // The database has now been created. Time to try to update it. EXPECT_TRUE(v4_database_); @@ -304,8 +306,7 @@ v4_database_->ApplyUpdate(std::move(parsed_server_response), callback_db_updated_); - task_runner_->RunPendingTasks(); - base::RunLoop().RunUntilIdle(); + WaitForTasksOnTaskRunner(); VerifyExpectedStoresState(false); } @@ -317,8 +318,7 @@ V4Database::Create(task_runner_, database_dirname_, list_infos_, callback_db_ready_); created_but_not_called_back_ = true; - task_runner_->RunPendingTasks(); - base::RunLoop().RunUntilIdle(); + WaitForTasksOnTaskRunner(); // The database has now been created. Time to try to update it. EXPECT_TRUE(v4_database_); @@ -333,8 +333,7 @@ v4_database_->ApplyUpdate( CreateFakeServerResponse(expected_store_state_map_, false), callback_db_updated_); - task_runner_->RunPendingTasks(); - base::RunLoop().RunUntilIdle(); + WaitForTasksOnTaskRunner(); VerifyExpectedStoresState(false); } @@ -347,9 +346,7 @@ V4Database::Create(task_runner_, database_dirname_, list_infos_, callback_db_ready_); created_but_not_called_back_ = true; - task_runner_->RunPendingTasks(); - - base::RunLoop().RunUntilIdle(); + WaitForTasksOnTaskRunner(); EXPECT_EQ(true, created_and_called_back_); StoresToCheck stores_to_check({linux_malware_id_, win_malware_id_}); @@ -372,9 +369,7 @@ V4Database::Create(task_runner_, database_dirname_, list_infos_, callback_db_ready_); created_but_not_called_back_ = true; - task_runner_->RunPendingTasks(); - - base::RunLoop().RunUntilIdle(); + WaitForTasksOnTaskRunner(); EXPECT_EQ(true, created_and_called_back_); StoreAndHashPrefixes store_and_hash_prefixes; @@ -393,9 +388,7 @@ V4Database::Create(task_runner_, database_dirname_, list_infos_, callback_db_ready_); created_but_not_called_back_ = true; - task_runner_->RunPendingTasks(); - - base::RunLoop().RunUntilIdle(); + WaitForTasksOnTaskRunner(); EXPECT_EQ(true, created_and_called_back_); // Set the store corresponding to linux_malware_id_ to match the full hash. @@ -422,9 +415,7 @@ V4Database::Create(task_runner_, database_dirname_, list_infos_, callback_db_ready_); created_but_not_called_back_ = true; - task_runner_->RunPendingTasks(); - - base::RunLoop().RunUntilIdle(); + WaitForTasksOnTaskRunner(); EXPECT_EQ(true, created_and_called_back_); // Don't add win_malware_id_ to the StoresToCheck. @@ -443,9 +434,7 @@ V4Database::Create(task_runner_, database_dirname_, list_infos_, callback_db_ready_); created_but_not_called_back_ = true; - task_runner_->RunPendingTasks(); - - base::RunLoop().RunUntilIdle(); + WaitForTasksOnTaskRunner(); EXPECT_EQ(true, created_and_called_back_); // verify_checksum_called_back_ set to false in the constructor. @@ -457,8 +446,7 @@ // verify_checksum_called_back_ should still be false since the checksum // verification is async. EXPECT_FALSE(verify_checksum_called_back_); - task_runner_->RunPendingTasks(); - base::RunLoop().RunUntilIdle(); + WaitForTasksOnTaskRunner(); EXPECT_TRUE(verify_checksum_called_back_); }
diff --git a/components/safe_browsing_db/v4_local_database_manager.cc b/components/safe_browsing_db/v4_local_database_manager.cc index 65916ba..aca22fc 100644 --- a/components/safe_browsing_db/v4_local_database_manager.cc +++ b/components/safe_browsing_db/v4_local_database_manager.cc
@@ -26,6 +26,8 @@ std::numeric_limits<ThreatSeverity>::max(); ListInfos GetListInfos() { + // NOTE(vakh): When adding a store here, add the corresponding store-specific + // histograms also. return ListInfos( {ListInfo(true, "UrlMalware.store", GetUrlMalwareId(), SB_THREAT_TYPE_URL_MALWARE), @@ -79,7 +81,10 @@ } V4LocalDatabaseManager::V4LocalDatabaseManager(const base::FilePath& base_path) - : base_path_(base_path), enabled_(false), list_infos_(GetListInfos()) { + : base_path_(base_path), + enabled_(false), + list_infos_(GetListInfos()), + weak_factory_(this) { DCHECK(!base_path_.empty()); DCHECK(!list_infos_.empty()); @@ -149,7 +154,13 @@ return true; } - PerformFullHashCheck(std::move(check), full_hash_to_store_and_hash_prefixes); + // Post the task to check full hashes back on the IO thread to follow the + // documented behavior of CheckBrowseUrl. + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&V4LocalDatabaseManager::PerformFullHashCheck, this, + base::Passed(std::move(check)), + full_hash_to_store_and_hash_prefixes)); return false; } @@ -239,7 +250,7 @@ SafeBrowsingDatabaseManager::StartOnIOThread(request_context_getter, config); db_updated_callback_ = base::Bind(&V4LocalDatabaseManager::DatabaseUpdated, - base::Unretained(this)); + weak_factory_.GetWeakPtr()); SetupUpdateProtocolManager(request_context_getter, config); SetupDatabase(); @@ -289,7 +300,7 @@ // database updates. v4_database_->VerifyChecksum( base::Bind(&V4LocalDatabaseManager::DatabaseReadyForUpdates, - base::Unretained(this))); + weak_factory_.GetWeakPtr())); ProcessQueuedChecks(); } else { @@ -424,7 +435,7 @@ v4_get_hash_protocol_manager_->GetFullHashes( full_hash_to_store_and_hash_prefixes, base::Bind(&V4LocalDatabaseManager::OnFullHashResponse, - base::Unretained(this), base::Passed(std::move(check)))); + weak_factory_.GetWeakPtr(), base::Passed(std::move(check)))); } void V4LocalDatabaseManager::ProcessQueuedChecks() { @@ -472,16 +483,18 @@ // Do not create the database on the IO thread since this may be an expensive // operation. Instead, do that on the task_runner and when the new database // has been created, swap it out on the IO thread. - NewDatabaseReadyCallback db_ready_callback = base::Bind( - &V4LocalDatabaseManager::DatabaseReadyForChecks, base::Unretained(this)); + NewDatabaseReadyCallback db_ready_callback = + base::Bind(&V4LocalDatabaseManager::DatabaseReadyForChecks, + weak_factory_.GetWeakPtr()); V4Database::Create(task_runner_, base_path_, list_infos_, db_ready_callback); } void V4LocalDatabaseManager::SetupUpdateProtocolManager( net::URLRequestContextGetter* request_context_getter, const V4ProtocolConfig& config) { - V4UpdateCallback callback = base::Bind( - &V4LocalDatabaseManager::UpdateRequestCompleted, base::Unretained(this)); + V4UpdateCallback callback = + base::Bind(&V4LocalDatabaseManager::UpdateRequestCompleted, + weak_factory_.GetWeakPtr()); v4_update_protocol_manager_ = V4UpdateProtocolManager::Create(request_context_getter, config, callback);
diff --git a/components/safe_browsing_db/v4_local_database_manager.h b/components/safe_browsing_db/v4_local_database_manager.h index 6b9a997..a5bdef4 100644 --- a/components/safe_browsing_db/v4_local_database_manager.h +++ b/components/safe_browsing_db/v4_local_database_manager.h
@@ -10,6 +10,7 @@ #include <memory> +#include "base/memory/weak_ptr.h" #include "components/safe_browsing_db/database_manager.h" #include "components/safe_browsing_db/hit_report.h" #include "components/safe_browsing_db/v4_database.h" @@ -70,6 +71,13 @@ // Must be initialized by calling StartOnIOThread() before using. V4LocalDatabaseManager(const base::FilePath& base_path); + ~V4LocalDatabaseManager() override; + + void SetTaskRunnerForTest( + const scoped_refptr<base::SequencedTaskRunner>& task_runner) { + task_runner_ = task_runner; + } + enum class ClientCallbackType { // This represents the case when we're trying to determine if a URL is // unsafe from the following perspectives: Malware, Phishing, UwS. @@ -120,10 +128,6 @@ private: friend class V4LocalDatabaseManagerTest; - void SetTaskRunnerForTest( - const scoped_refptr<base::SequencedTaskRunner>& task_runner) { - task_runner_ = task_runner; - } FRIEND_TEST_ALL_PREFIXES(V4LocalDatabaseManagerTest, TestGetSeverestThreatTypeAndMetadata); @@ -131,8 +135,6 @@ // which clients have cancelled their outstanding request. typedef std::unordered_set<Client*> PendingClients; - ~V4LocalDatabaseManager() override; - // Called when all the stores managed by the database have been read from // disk after startup and the database is ready for checking resource // reputation. @@ -170,9 +172,9 @@ const std::vector<FullHashInfo>& full_hash_infos); // Performs the full hash checking of the URL in |check|. - void PerformFullHashCheck(std::unique_ptr<PendingCheck> check, - const FullHashToStoreAndHashPrefixesMap& - full_hash_to_store_and_hash_prefixes); + virtual void PerformFullHashCheck(std::unique_ptr<PendingCheck> check, + const FullHashToStoreAndHashPrefixesMap& + full_hash_to_store_and_hash_prefixes); // When the database is ready to use, process the checks that were queued // while the database was loading from disk. @@ -234,6 +236,8 @@ // The protocol manager that downloads the hash prefix updates. std::unique_ptr<V4UpdateProtocolManager> v4_update_protocol_manager_; + base::WeakPtrFactory<V4LocalDatabaseManager> weak_factory_; + friend class base::RefCountedThreadSafe<V4LocalDatabaseManager>; DISALLOW_COPY_AND_ASSIGN(V4LocalDatabaseManager); }; // class V4LocalDatabaseManager
diff --git a/components/safe_browsing_db/v4_local_database_manager_unittest.cc b/components/safe_browsing_db/v4_local_database_manager_unittest.cc index 4c572bc..b6659f4b1d 100644 --- a/components/safe_browsing_db/v4_local_database_manager_unittest.cc +++ b/components/safe_browsing_db/v4_local_database_manager_unittest.cc
@@ -79,6 +79,31 @@ GURL expected_url; }; +class FakeV4LocalDatabaseManager : public V4LocalDatabaseManager { + public: + void PerformFullHashCheck(std::unique_ptr<PendingCheck> check, + const FullHashToStoreAndHashPrefixesMap& + full_hash_to_store_and_hash_prefixes) override { + perform_full_hash_check_called_ = true; + } + + FakeV4LocalDatabaseManager(const base::FilePath& base_path) + : V4LocalDatabaseManager(base_path), + perform_full_hash_check_called_(false) {} + + static bool PerformFullHashCheckCalled( + scoped_refptr<safe_browsing::V4LocalDatabaseManager>& v4_ldbm) { + FakeV4LocalDatabaseManager* fake = + static_cast<FakeV4LocalDatabaseManager*>(v4_ldbm.get()); + return fake->perform_full_hash_check_called_; + } + + private: + ~FakeV4LocalDatabaseManager() override {} + + bool perform_full_hash_check_called_; +}; + class V4LocalDatabaseManagerTest : public PlatformTest { public: V4LocalDatabaseManagerTest() : task_runner_(new base::TestSimpleTaskRunner) {} @@ -91,7 +116,7 @@ v4_local_database_manager_ = make_scoped_refptr(new V4LocalDatabaseManager(base_dir_.GetPath())); - v4_local_database_manager_->SetTaskRunnerForTest(task_runner_); + SetTaskRunnerForTest(); StartLocalDatabaseManager(); } @@ -138,16 +163,21 @@ V4Database::Destroy(std::move(v4_local_database_manager_->v4_database_)); } + void SetTaskRunnerForTest() { + v4_local_database_manager_->SetTaskRunnerForTest(task_runner_); + } + void StartLocalDatabaseManager() { v4_local_database_manager_->StartOnIOThread(NULL, V4ProtocolConfig()); } void StopLocalDatabaseManager() { - v4_local_database_manager_->StopOnIOThread(true); + if (v4_local_database_manager_) { + v4_local_database_manager_->StopOnIOThread(true); + } // Force destruction of the database. - task_runner_->RunPendingTasks(); - base::RunLoop().RunUntilIdle(); + WaitForTasksOnTaskRunner(); } void WaitForTasksOnTaskRunner() { @@ -205,6 +235,9 @@ // The fake database returns a matched hash prefix. EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( GURL("http://example.com/a/"), nullptr)); + + // Wait for PerformFullHashCheck to complete. + WaitForTasksOnTaskRunner(); } TEST_F(V4LocalDatabaseManagerTest, @@ -268,4 +301,66 @@ EXPECT_TRUE(GetQueuedChecks().empty()); } +// This test is somewhat similar to TestCheckBrowseUrlWithFakeDbReturnsMatch but +// it uses a fake V4LocalDatabaseManager to assert that PerformFullHashCheck is +// called async. +TEST_F(V4LocalDatabaseManagerTest, PerformFullHashCheckCalledAsync) { + // StopLocalDatabaseManager before resetting it because that's what + // ~V4LocalDatabaseManager expects. + StopLocalDatabaseManager(); + v4_local_database_manager_ = + make_scoped_refptr(new FakeV4LocalDatabaseManager(base_dir_.GetPath())); + SetTaskRunnerForTest(); + StartLocalDatabaseManager(); + WaitForTasksOnTaskRunner(); + net::TestURLFetcherFactory factory; + + StoreAndHashPrefixes store_and_hash_prefixes; + store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), HashPrefix("aaaa")); + ReplaceV4Database(store_and_hash_prefixes); + + // The fake database returns a matched hash prefix. + EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( + GURL("http://example.com/a/"), nullptr)); + + EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled( + v4_local_database_manager_)); + + // Wait for PerformFullHashCheck to complete. + WaitForTasksOnTaskRunner(); + + EXPECT_TRUE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled( + v4_local_database_manager_)); +} + +TEST_F(V4LocalDatabaseManagerTest, UsingWeakPtrDropsCallback) { + // StopLocalDatabaseManager before resetting it because that's what + // ~V4LocalDatabaseManager expects. + StopLocalDatabaseManager(); + v4_local_database_manager_ = + make_scoped_refptr(new FakeV4LocalDatabaseManager(base_dir_.GetPath())); + SetTaskRunnerForTest(); + StartLocalDatabaseManager(); + WaitForTasksOnTaskRunner(); + net::TestURLFetcherFactory factory; + + StoreAndHashPrefixes store_and_hash_prefixes; + store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), HashPrefix("aaaa")); + ReplaceV4Database(store_and_hash_prefixes); + + // The fake database returns a matched hash prefix. + EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( + GURL("http://example.com/a/"), nullptr)); + v4_local_database_manager_->StopOnIOThread(true); + + // Release the V4LocalDatabaseManager object right away before the callback + // gets called. When the callback gets called, without using a weak-ptr + // factory, this leads to a use after free. However, using the weak-ptr means + // that the callback is simply dropped. + v4_local_database_manager_ = nullptr; + + // Wait for the tasks scheduled by StopOnIOThread to complete. + WaitForTasksOnTaskRunner(); +} + } // namespace safe_browsing
diff --git a/components/safe_browsing_db/v4_store.cc b/components/safe_browsing_db/v4_store.cc index 81f30ec7..49ec3c5 100644 --- a/components/safe_browsing_db/v4_store.cc +++ b/components/safe_browsing_db/v4_store.cc
@@ -36,8 +36,12 @@ // Part 3: Represent the unit of value being measured and logged. const char kResult[] = ".Result"; const char kTime[] = ".Time"; +// Part 4 (optional): Represent the name of the list for which the metric is +// being logged. For instance, ".UrlSoceng". // UMA metric names for this file are generated by appending one value each, -// in order, from parts 1, 2, and 3. +// in order, from parts [1, 2, and 3], or [1, 2, 3, and 4]. For example: +// SafeBrowsing.V4ProcessPartialUpdate.ApplyUpdate.Result, or +// SafeBrowsing.V4ProcessPartialUpdate.ApplyUpdate.Result.UrlSoceng const uint32_t kFileMagic = 0x600D71FE; const uint32_t kFileVersion = 9; @@ -65,7 +69,7 @@ std::string suffix = GetUmaSuffixForStore(file_path); base::HistogramBase* histogram_suffix = base::Histogram::FactoryTimeGet( - metric + suffix + kTime, base::TimeDelta::FromMilliseconds(1), + metric + kTime + suffix, base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1), kBucketCount, base::HistogramBase::kUmaTargetedHistogramFlag); if (histogram_suffix) { @@ -89,7 +93,7 @@ std::string suffix = GetUmaSuffixForStore(file_path); base::HistogramBase* histogram_suffix = base::LinearHistogram::FactoryGet( - metric + suffix + kResult, 1, maximum, maximum + 1, + metric + kResult + suffix, 1, maximum, maximum + 1, base::HistogramBase::kUmaTargetedHistogramFlag); if (histogram_suffix) { histogram_suffix->Add(value); @@ -194,7 +198,9 @@ const base::FilePath& store_path) : store_path_(store_path), task_runner_(task_runner) {} -V4Store::~V4Store() {} +V4Store::~V4Store() { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); +} std::string V4Store::DebugString() const { std::string state_base64; @@ -204,6 +210,14 @@ store_path_.value().c_str(), state_base64.c_str()); } +// static +void V4Store::Destroy(std::unique_ptr<V4Store> v4_store) { + V4Store* v4_store_raw = v4_store.release(); + if (v4_store_raw) { + v4_store_raw->task_runner_->DeleteSoon(FROM_HERE, v4_store_raw); + } +} + void V4Store::Reset() { expected_checksum_.clear(); hash_prefix_map_.clear();
diff --git a/components/safe_browsing_db/v4_store.h b/components/safe_browsing_db/v4_store.h index 1b9e4d6..9775ee7 100644 --- a/components/safe_browsing_db/v4_store.h +++ b/components/safe_browsing_db/v4_store.h
@@ -176,6 +176,10 @@ std::string DebugString() const; + // Schedules the destruction of the V4Store object pointed to by |v4_store|, + // on the task runner. + static void Destroy(std::unique_ptr<V4Store> v4_store); + // Reads the store file from disk and populates the in-memory representation // of the hash prefixes. void Initialize();
diff --git a/components/search_engines/template_url_service.cc b/components/search_engines/template_url_service.cc index d6c5cb96..c7d94214 100644 --- a/components/search_engines/template_url_service.cc +++ b/components/search_engines/template_url_service.cc
@@ -2115,8 +2115,8 @@ if (!loaded_) return; - FOR_EACH_OBSERVER(TemplateURLServiceObserver, model_observers_, - OnTemplateURLServiceChanged()); + for (auto& observer : model_observers_) + observer.OnTemplateURLServiceChanged(); } // |template_urls| are the TemplateURLs loaded from the database.
diff --git a/components/search_provider_logos/logo_tracker.cc b/components/search_provider_logos/logo_tracker.cc index 2027d8a..0facd981 100644 --- a/components/search_provider_logos/logo_tracker.cc +++ b/components/search_provider_logos/logo_tracker.cc
@@ -157,7 +157,8 @@ is_cached_logo_valid_ = false; // Clear obsevers. - FOR_EACH_OBSERVER(LogoObserver, logo_observers_, OnObserverRemoved()); + for (auto& observer : logo_observers_) + observer.OnObserverRemoved(); logo_observers_.Clear(); } @@ -186,7 +187,8 @@ } is_cached_logo_valid_ = true; Logo* logo = cached_logo_.get(); - FOR_EACH_OBSERVER(LogoObserver, logo_observers_, OnLogoAvailable(logo, true)); + for (auto& observer : logo_observers_) + observer.OnLogoAvailable(logo, true); FetchLogo(); } @@ -294,9 +296,8 @@ // Notify observers if a new logo was fetched, or if the new logo is NULL // but the cached logo was non-NULL. if (logo || cached_logo_) { - FOR_EACH_OBSERVER(LogoObserver, - logo_observers_, - OnLogoAvailable(logo.get(), false)); + for (auto& observer : logo_observers_) + observer.OnLogoAvailable(logo.get(), false); SetCachedLogo(std::move(encoded_logo)); } }
diff --git a/components/session_manager/OWNERS b/components/session_manager/OWNERS index e7a8a06..b280edc9 100644 --- a/components/session_manager/OWNERS +++ b/components/session_manager/OWNERS
@@ -1,2 +1,3 @@ achuith@chromium.org alemate@chromium.org +xiyuan@chromium.org
diff --git a/components/sessions/core/tab_restore_service_helper.cc b/components/sessions/core/tab_restore_service_helper.cc index 05094d2..841f075 100644 --- a/components/sessions/core/tab_restore_service_helper.cc +++ b/components/sessions/core/tab_restore_service_helper.cc
@@ -51,8 +51,8 @@ } TabRestoreServiceHelper::~TabRestoreServiceHelper() { - FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_, - TabRestoreServiceDestroyed(tab_restore_service_)); + for (auto& observer : observer_list_) + observer.TabRestoreServiceDestroyed(tab_restore_service_); } void TabRestoreServiceHelper::AddObserver( @@ -265,13 +265,13 @@ } void TabRestoreServiceHelper::NotifyTabsChanged() { - FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_, - TabRestoreServiceChanged(tab_restore_service_)); + for (auto& observer : observer_list_) + observer.TabRestoreServiceChanged(tab_restore_service_); } void TabRestoreServiceHelper::NotifyLoaded() { - FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_, - TabRestoreServiceLoaded(tab_restore_service_)); + for (auto& observer : observer_list_) + observer.TabRestoreServiceLoaded(tab_restore_service_); } void TabRestoreServiceHelper::AddEntry(std::unique_ptr<Entry> entry,
diff --git a/components/ssl_errors/BUILD.gn b/components/ssl_errors/BUILD.gn index d58ffa3..eda7b32f 100644 --- a/components/ssl_errors/BUILD.gn +++ b/components/ssl_errors/BUILD.gn
@@ -31,6 +31,7 @@ ":ssl_errors", "//base", "//components/network_time", + "//components/network_time:network_time_test_support", "//components/prefs:test_support", "//net:test_support", "//testing/gtest",
diff --git a/components/ssl_errors/error_classification.cc b/components/ssl_errors/error_classification.cc index 66aa42d..c61b326 100644 --- a/components/ssl_errors/error_classification.cc +++ b/components/ssl_errors/error_classification.cc
@@ -38,39 +38,6 @@ namespace ssl_errors { namespace { -// Describes the result of getting network time and if it was -// unavailable, why it was unavailable. This enum is being histogrammed -// so do not reorder or remove values. -enum NetworkClockState { - // Value 0 was NETWORK_CLOCK_STATE_UNKNOWN_NO_SYNC, which is obsolete - // in favor of the finer-grained values below. - - // The clock state relative to network time is unknown because the - // user's clock has fallen out of sync with the latest information - // from the network (due to e.g. suspend/resume). - NETWORK_CLOCK_STATE_UNKNOWN_SYNC_LOST = 1, - // The clock is "close enough" to the network time. - NETWORK_CLOCK_STATE_OK, - // The clock is in the past relative to network time. - NETWORK_CLOCK_STATE_CLOCK_IN_PAST, - // The clock is in the future relative to network time. - NETWORK_CLOCK_STATE_CLOCK_IN_FUTURE, - // The clock state relative to network time is unknown because no sync - // attempt has been made yet. - NETWORK_CLOCK_STATE_UNKNOWN_NO_SYNC_ATTEMPT, - // The clock state relative to network time is unknown because one or - // more sync attempts has failed. - NETWORK_CLOCK_STATE_UNKNOWN_NO_SUCCESSFUL_SYNC, - // The clock state relative to network time is unknown because the - // first sync attempt is still pending. - NETWORK_CLOCK_STATE_UNKNOWN_FIRST_SYNC_PENDING, - // The clock state relative to network time is unknown because one or - // more time query attempts have failed, and a subsequent sync attempt - // is still pending. - NETWORK_CLOCK_STATE_UNKNOWN_SUBSEQUENT_SYNC_PENDING, - NETWORK_CLOCK_STATE_MAX -}; - // Events for UMA. Do not reorder or change! enum SSLInterstitialCause { CLOCK_PAST, @@ -280,8 +247,8 @@ build_time_state = CLOCK_STATE_FUTURE; } - UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.clockstate.network2", - network_time_result, NETWORK_CLOCK_STATE_MAX); + UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.clockstate.network3", + network_state, NETWORK_CLOCK_STATE_MAX); UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.clockstate.build_time", build_time_state, CLOCK_STATE_MAX);
diff --git a/components/ssl_errors/error_classification.h b/components/ssl_errors/error_classification.h index 9bcaeae..7da66fdd 100644 --- a/components/ssl_errors/error_classification.h +++ b/components/ssl_errors/error_classification.h
@@ -46,6 +46,41 @@ CLOCK_STATE_MAX, }; +// Describes the result of getting network time and if it was +// unavailable, why it was unavailable. This enum is being histogrammed +// so do not reorder or remove values. +// +// Exposed for testing. +enum NetworkClockState { + // Value 0 was NETWORK_CLOCK_STATE_UNKNOWN_NO_SYNC, which is obsolete + // in favor of the finer-grained values below. + + // The clock state relative to network time is unknown because the + // user's clock has fallen out of sync with the latest information + // from the network (due to e.g. suspend/resume). + NETWORK_CLOCK_STATE_UNKNOWN_SYNC_LOST = 1, + // The clock is "close enough" to the network time. + NETWORK_CLOCK_STATE_OK, + // The clock is in the past relative to network time. + NETWORK_CLOCK_STATE_CLOCK_IN_PAST, + // The clock is in the future relative to network time. + NETWORK_CLOCK_STATE_CLOCK_IN_FUTURE, + // The clock state relative to network time is unknown because no sync + // attempt has been made yet. + NETWORK_CLOCK_STATE_UNKNOWN_NO_SYNC_ATTEMPT, + // The clock state relative to network time is unknown because one or + // more sync attempts has failed. + NETWORK_CLOCK_STATE_UNKNOWN_NO_SUCCESSFUL_SYNC, + // The clock state relative to network time is unknown because the + // first sync attempt is still pending. + NETWORK_CLOCK_STATE_UNKNOWN_FIRST_SYNC_PENDING, + // The clock state relative to network time is unknown because one or + // more time query attempts have failed, and a subsequent sync attempt + // is still pending. + NETWORK_CLOCK_STATE_UNKNOWN_SUBSEQUENT_SYNC_PENDING, + NETWORK_CLOCK_STATE_MAX +}; + // Compares |now_system| to the build time and to the current network time, and // returns an inference about the state of the system clock. A result from // network time, if available, will always be preferred to a result from the
diff --git a/components/ssl_errors/error_classification_unittest.cc b/components/ssl_errors/error_classification_unittest.cc index 425a9f2e..e436f4e 100644 --- a/components/ssl_errors/error_classification_unittest.cc +++ b/components/ssl_errors/error_classification_unittest.cc
@@ -8,21 +8,41 @@ #include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/strings/string_split.h" +#include "base/test/histogram_tester.h" +#include "base/test/simple_test_clock.h" +#include "base/test/simple_test_tick_clock.h" #include "base/time/default_clock.h" #include "base/time/default_tick_clock.h" +#include "components/network_time/network_time_test_utils.h" #include "components/network_time/network_time_tracker.h" #include "components/prefs/testing_pref_service.h" #include "net/base/net_errors.h" #include "net/cert/x509_cert_types.h" #include "net/cert/x509_certificate.h" #include "net/test/cert_test_util.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "net/test/embedded_test_server/http_response.h" #include "net/test/test_certificate_data.h" #include "net/test/test_data_directory.h" #include "net/url_request/url_request_test_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" -class SSLErrorClassificationTest : public testing::Test {}; +namespace { +const char kNetworkTimeHistogram[] = "interstitial.ssl.clockstate.network3"; + +static std::unique_ptr<net::test_server::HttpResponse> +NetworkErrorResponseHandler(const net::test_server::HttpRequest& request) { + return std::unique_ptr<net::test_server::HttpResponse>( + new net::test_server::RawHttpResponse("", "")); +} + +} // namespace + +class SSLErrorClassificationTest : public network_time::FieldTrialTest { + public: + SSLErrorClassificationTest() : network_time::FieldTrialTest() {} +}; TEST_F(SSLErrorClassificationTest, TestNameMismatch) { scoped_refptr<net::X509Certificate> google_cert( @@ -189,6 +209,10 @@ TEST_F(SSLErrorClassificationTest, GetClockState) { // This test aims to obtain all possible return values of // |GetClockState|. + const char kBuildTimeHistogram[] = "interstitial.ssl.clockstate.build_time"; + base::HistogramTester histograms; + histograms.ExpectTotalCount(kBuildTimeHistogram, 0); + histograms.ExpectTotalCount(kNetworkTimeHistogram, 0); TestingPrefServiceSimple pref_service; network_time::NetworkTimeTracker::RegisterPrefs(pref_service.registry()); base::MessageLoop loop; @@ -201,18 +225,39 @@ EXPECT_EQ( ssl_errors::ClockState::CLOCK_STATE_UNKNOWN, ssl_errors::GetClockState(base::Time::Now(), &network_time_tracker)); + histograms.ExpectTotalCount(kBuildTimeHistogram, 1); + histograms.ExpectBucketCount(kBuildTimeHistogram, + ssl_errors::ClockState::CLOCK_STATE_UNKNOWN, 1); + histograms.ExpectTotalCount(kNetworkTimeHistogram, 1); + histograms.ExpectBucketCount( + kNetworkTimeHistogram, + ssl_errors::NETWORK_CLOCK_STATE_UNKNOWN_NO_SYNC_ATTEMPT, 1); ssl_errors::SetBuildTimeForTesting(base::Time::Now() - base::TimeDelta::FromDays(367)); EXPECT_EQ( ssl_errors::ClockState::CLOCK_STATE_FUTURE, ssl_errors::GetClockState(base::Time::Now(), &network_time_tracker)); + histograms.ExpectTotalCount(kBuildTimeHistogram, 2); + histograms.ExpectBucketCount(kBuildTimeHistogram, + ssl_errors::ClockState::CLOCK_STATE_FUTURE, 1); + histograms.ExpectTotalCount(kNetworkTimeHistogram, 2); + histograms.ExpectBucketCount( + kNetworkTimeHistogram, + ssl_errors::NETWORK_CLOCK_STATE_UNKNOWN_NO_SYNC_ATTEMPT, 2); ssl_errors::SetBuildTimeForTesting(base::Time::Now() + base::TimeDelta::FromDays(3)); EXPECT_EQ( ssl_errors::ClockState::CLOCK_STATE_PAST, ssl_errors::GetClockState(base::Time::Now(), &network_time_tracker)); + histograms.ExpectTotalCount(kBuildTimeHistogram, 3); + histograms.ExpectBucketCount(kBuildTimeHistogram, + ssl_errors::ClockState::CLOCK_STATE_FUTURE, 1); + histograms.ExpectTotalCount(kNetworkTimeHistogram, 3); + histograms.ExpectBucketCount( + kNetworkTimeHistogram, + ssl_errors::NETWORK_CLOCK_STATE_UNKNOWN_NO_SYNC_ATTEMPT, 3); // Intentionally leave the build time alone. It should be ignored // in favor of network time. @@ -224,6 +269,10 @@ EXPECT_EQ( ssl_errors::ClockState::CLOCK_STATE_PAST, ssl_errors::GetClockState(base::Time::Now(), &network_time_tracker)); + histograms.ExpectTotalCount(kBuildTimeHistogram, 4); + histograms.ExpectTotalCount(kNetworkTimeHistogram, 4); + histograms.ExpectBucketCount( + kNetworkTimeHistogram, ssl_errors::NETWORK_CLOCK_STATE_CLOCK_IN_PAST, 1); network_time_tracker.UpdateNetworkTime( base::Time::Now() - base::TimeDelta::FromHours(1), @@ -233,6 +282,11 @@ EXPECT_EQ( ssl_errors::ClockState::CLOCK_STATE_FUTURE, ssl_errors::GetClockState(base::Time::Now(), &network_time_tracker)); + histograms.ExpectTotalCount(kBuildTimeHistogram, 5); + histograms.ExpectTotalCount(kNetworkTimeHistogram, 5); + histograms.ExpectBucketCount(kNetworkTimeHistogram, + ssl_errors::NETWORK_CLOCK_STATE_CLOCK_IN_FUTURE, + 1); network_time_tracker.UpdateNetworkTime( base::Time::Now(), @@ -242,6 +296,10 @@ EXPECT_EQ( ssl_errors::ClockState::CLOCK_STATE_OK, ssl_errors::GetClockState(base::Time::Now(), &network_time_tracker)); + histograms.ExpectTotalCount(kBuildTimeHistogram, 6); + histograms.ExpectTotalCount(kNetworkTimeHistogram, 6); + histograms.ExpectBucketCount(kNetworkTimeHistogram, + ssl_errors::NETWORK_CLOCK_STATE_OK, 1); // Now clear the network time. The build time should reassert // itself. @@ -263,3 +321,123 @@ ssl_errors::ClockState::CLOCK_STATE_UNKNOWN, ssl_errors::GetClockState(base::Time::Now(), &network_time_tracker)); } + +// Tests that all possible NetworkClockState histogram values are recorded +// appropriately. +TEST_F(SSLErrorClassificationTest, NetworkClockStateHistogram) { + base::Thread io_thread("IO thread"); + base::Thread::Options thread_options; + thread_options.message_loop_type = base::MessageLoop::TYPE_IO; + EXPECT_TRUE(io_thread.StartWithOptions(thread_options)); + + net::EmbeddedTestServer test_server; + ASSERT_TRUE(test_server.Start()); + + base::HistogramTester histograms; + histograms.ExpectTotalCount(kNetworkTimeHistogram, 0); + TestingPrefServiceSimple pref_service; + network_time::NetworkTimeTracker::RegisterPrefs(pref_service.registry()); + base::SimpleTestTickClock* tick_clock = new base::SimpleTestTickClock; + base::SimpleTestClock* clock = new base::SimpleTestClock; + // Do this to be sure that |is_null| returns false. + clock->Advance(base::TimeDelta::FromDays(111)); + tick_clock->Advance(base::TimeDelta::FromDays(222)); + + base::MessageLoop loop; + network_time::NetworkTimeTracker network_time_tracker( + std::unique_ptr<base::Clock>(clock), + std::unique_ptr<base::TickClock>(tick_clock), &pref_service, + new net::TestURLRequestContextGetter(io_thread.task_runner())); + network_time_tracker.SetTimeServerURLForTesting(test_server.GetURL("/")); + SetNetworkQueriesWithVariationsService(true, 0.0); + + // No sync attempt. + EXPECT_EQ( + ssl_errors::ClockState::CLOCK_STATE_UNKNOWN, + ssl_errors::GetClockState(base::Time::Now(), &network_time_tracker)); + histograms.ExpectTotalCount(kNetworkTimeHistogram, 1); + histograms.ExpectBucketCount( + kNetworkTimeHistogram, + ssl_errors::NETWORK_CLOCK_STATE_UNKNOWN_NO_SYNC_ATTEMPT, 1); + + // First sync attempt is pending. + test_server.RegisterRequestHandler(base::Bind(&NetworkErrorResponseHandler)); + EXPECT_TRUE(network_time_tracker.QueryTimeServiceForTesting()); + EXPECT_EQ( + ssl_errors::ClockState::CLOCK_STATE_UNKNOWN, + ssl_errors::GetClockState(base::Time::Now(), &network_time_tracker)); + histograms.ExpectTotalCount(kNetworkTimeHistogram, 2); + histograms.ExpectBucketCount( + kNetworkTimeHistogram, + ssl_errors::NETWORK_CLOCK_STATE_UNKNOWN_FIRST_SYNC_PENDING, 1); + network_time_tracker.WaitForFetchForTesting(123123123); + + // No successful sync. + EXPECT_EQ( + ssl_errors::ClockState::CLOCK_STATE_UNKNOWN, + ssl_errors::GetClockState(base::Time::Now(), &network_time_tracker)); + histograms.ExpectTotalCount(kNetworkTimeHistogram, 3); + histograms.ExpectBucketCount( + kNetworkTimeHistogram, + ssl_errors::NETWORK_CLOCK_STATE_UNKNOWN_NO_SUCCESSFUL_SYNC, 1); + + // Subsequent sync attempt is pending. + EXPECT_TRUE(network_time_tracker.QueryTimeServiceForTesting()); + EXPECT_EQ( + ssl_errors::ClockState::CLOCK_STATE_UNKNOWN, + ssl_errors::GetClockState(base::Time::Now(), &network_time_tracker)); + histograms.ExpectTotalCount(kNetworkTimeHistogram, 4); + histograms.ExpectBucketCount( + kNetworkTimeHistogram, + ssl_errors::NETWORK_CLOCK_STATE_UNKNOWN_SUBSEQUENT_SYNC_PENDING, 1); + network_time_tracker.WaitForFetchForTesting(123123123); + + // System clock is correct. + network_time_tracker.UpdateNetworkTime( + clock->Now(), + base::TimeDelta::FromSeconds(1), // resolution + base::TimeDelta::FromMilliseconds(250), // latency + tick_clock->NowTicks()); // posting time + EXPECT_EQ(ssl_errors::ClockState::CLOCK_STATE_OK, + ssl_errors::GetClockState(clock->Now(), &network_time_tracker)); + histograms.ExpectTotalCount(kNetworkTimeHistogram, 5); + histograms.ExpectBucketCount(kNetworkTimeHistogram, + ssl_errors::NETWORK_CLOCK_STATE_OK, 1); + + // System clock is in the past. + network_time_tracker.UpdateNetworkTime( + clock->Now() + base::TimeDelta::FromHours(1), + base::TimeDelta::FromSeconds(1), // resolution + base::TimeDelta::FromMilliseconds(250), // latency + tick_clock->NowTicks()); // posting time + EXPECT_EQ(ssl_errors::ClockState::CLOCK_STATE_PAST, + ssl_errors::GetClockState(clock->Now(), &network_time_tracker)); + histograms.ExpectTotalCount(kNetworkTimeHistogram, 6); + histograms.ExpectBucketCount( + kNetworkTimeHistogram, ssl_errors::NETWORK_CLOCK_STATE_CLOCK_IN_PAST, 1); + + // System clock is in the future. + network_time_tracker.UpdateNetworkTime( + clock->Now() - base::TimeDelta::FromHours(1), + base::TimeDelta::FromSeconds(1), // resolution + base::TimeDelta::FromMilliseconds(250), // latency + tick_clock->NowTicks()); // posting time + EXPECT_EQ(ssl_errors::ClockState::CLOCK_STATE_FUTURE, + ssl_errors::GetClockState(clock->Now(), &network_time_tracker)); + histograms.ExpectTotalCount(kNetworkTimeHistogram, 7); + histograms.ExpectBucketCount(kNetworkTimeHistogram, + ssl_errors::NETWORK_CLOCK_STATE_CLOCK_IN_FUTURE, + 1); + + // Sync has been lost. + tick_clock->Advance(base::TimeDelta::FromSeconds(1)); + clock->Advance(base::TimeDelta::FromDays(1)); + // GetClockState() will fall back to the build time heuristic. + ssl_errors::GetClockState(clock->Now(), &network_time_tracker); + histograms.ExpectTotalCount(kNetworkTimeHistogram, 8); + histograms.ExpectBucketCount( + kNetworkTimeHistogram, ssl_errors::NETWORK_CLOCK_STATE_UNKNOWN_SYNC_LOST, + 1); + + io_thread.Stop(); +}
diff --git a/components/syncable_prefs/pref_model_associator.cc b/components/syncable_prefs/pref_model_associator.cc index 08392b3..a704131 100644 --- a/components/syncable_prefs/pref_model_associator.cc +++ b/components/syncable_prefs/pref_model_associator.cc
@@ -538,8 +538,8 @@ if (observer_iter == synced_pref_observers_.end()) return; SyncedPrefObserverList* observers = observer_iter->second.get(); - FOR_EACH_OBSERVER(SyncedPrefObserver, *observers, - OnSyncedPrefChanged(path, from_sync)); + for (auto& observer : *observers) + observer.OnSyncedPrefChanged(path, from_sync); } } // namespace syncable_prefs
diff --git a/components/syncable_prefs/pref_service_syncable.cc b/components/syncable_prefs/pref_service_syncable.cc index ab5e156..3aca8af 100644 --- a/components/syncable_prefs/pref_service_syncable.cc +++ b/components/syncable_prefs/pref_service_syncable.cc
@@ -180,8 +180,8 @@ } void PrefServiceSyncable::OnIsSyncingChanged() { - FOR_EACH_OBSERVER(PrefServiceSyncableObserver, observer_list_, - OnIsSyncingChanged()); + for (auto& observer : observer_list_) + observer.OnIsSyncingChanged(); } void PrefServiceSyncable::ProcessPrefChange(const std::string& name) {
diff --git a/components/toolbar/toolbar_model_impl.cc b/components/toolbar/toolbar_model_impl.cc index 5e795f8..1d6b117 100644 --- a/components/toolbar/toolbar_model_impl.cc +++ b/components/toolbar/toolbar_model_impl.cc
@@ -110,6 +110,8 @@ base::string16 ToolbarModelImpl::GetSecureVerboseText() const { switch (GetSecurityLevel(false)) { + case SecurityStateModel::HTTP_SHOW_WARNING: + return l10n_util::GetStringUTF16(IDS_NOT_SECURE_VERBOSE_STATE); case SecurityStateModel::SECURE: return l10n_util::GetStringUTF16(IDS_SECURE_VERBOSE_STATE); case SecurityStateModel::DANGEROUS:
diff --git a/components/undo/undo_manager.cc b/components/undo/undo_manager.cc index 59cb8e0..6829e52 100644 --- a/components/undo/undo_manager.cc +++ b/components/undo/undo_manager.cc
@@ -203,8 +203,8 @@ } void UndoManager::NotifyOnUndoManagerStateChange() { - FOR_EACH_OBSERVER( - UndoManagerObserver, observers_, OnUndoManagerStateChange()); + for (auto& observer : observers_) + observer.OnUndoManagerStateChange(); } void UndoManager::AddUndoGroup(UndoGroup* new_undo_group) {
diff --git a/components/update_client/update_client.cc b/components/update_client/update_client.cc index db4b21b..848e539f 100644 --- a/components/update_client/update_client.cc +++ b/components/update_client/update_client.cc
@@ -187,7 +187,8 @@ void UpdateClientImpl::NotifyObservers(Observer::Events event, const std::string& id) { DCHECK(thread_checker_.CalledOnValidThread()); - FOR_EACH_OBSERVER(Observer, observer_list_, OnEvent(event, id)); + for (auto& observer : observer_list_) + observer.OnEvent(event, id); } bool UpdateClientImpl::GetCrxUpdateState(const std::string& id,
diff --git a/components/user_manager/user_manager_base.cc b/components/user_manager/user_manager_base.cc index 562589ce..f3ddd7d84 100644 --- a/components/user_manager/user_manager_base.cc +++ b/components/user_manager/user_manager_base.cc
@@ -677,8 +677,8 @@ void UserManagerBase::NotifyLocalStateChanged() { DCHECK(task_runner_->RunsTasksOnCurrentThread()); - FOR_EACH_OBSERVER( - UserManager::Observer, observer_list_, LocalStateChanged(this)); + for (auto& observer : observer_list_) + observer.LocalStateChanged(this); } bool UserManagerBase::CanUserBeRemoved(const User* user) const { @@ -977,24 +977,21 @@ void UserManagerBase::NotifyActiveUserChanged(const User* active_user) { DCHECK(task_runner_->RunsTasksOnCurrentThread()); - FOR_EACH_OBSERVER(UserManager::UserSessionStateObserver, - session_state_observer_list_, - ActiveUserChanged(active_user)); + for (auto& observer : session_state_observer_list_) + observer.ActiveUserChanged(active_user); } void UserManagerBase::NotifyUserAddedToSession(const User* added_user, bool user_switch_pending) { DCHECK(task_runner_->RunsTasksOnCurrentThread()); - FOR_EACH_OBSERVER(UserManager::UserSessionStateObserver, - session_state_observer_list_, - UserAddedToSession(added_user)); + for (auto& observer : session_state_observer_list_) + observer.UserAddedToSession(added_user); } void UserManagerBase::NotifyActiveUserHashChanged(const std::string& hash) { DCHECK(task_runner_->RunsTasksOnCurrentThread()); - FOR_EACH_OBSERVER(UserManager::UserSessionStateObserver, - session_state_observer_list_, - ActiveUserHashChanged(hash)); + for (auto& observer : session_state_observer_list_) + observer.ActiveUserHashChanged(hash); } void UserManagerBase::ChangeUserChildStatus(User* user, bool is_child) { @@ -1005,9 +1002,8 @@ SaveUserType(user->GetAccountId(), is_child ? user_manager::USER_TYPE_CHILD : user_manager::USER_TYPE_REGULAR); - FOR_EACH_OBSERVER(UserManager::UserSessionStateObserver, - session_state_observer_list_, - UserChangedChildStatus(user)); + for (auto& observer : session_state_observer_list_) + observer.UserChangedChildStatus(user); } void UserManagerBase::Initialize() {
diff --git a/components/user_prefs/tracked/segregated_pref_store.cc b/components/user_prefs/tracked/segregated_pref_store.cc index af8dfe3..edb1f20 100644 --- a/components/user_prefs/tracked/segregated_pref_store.cc +++ b/components/user_prefs/tracked/segregated_pref_store.cc
@@ -24,8 +24,8 @@ if (failed_sub_initializations_ + successful_sub_initializations_ < 2) return; - FOR_EACH_OBSERVER(PrefStore::Observer, outer_->observers_, - OnPrefValueChanged(key)); + for (auto& observer : outer_->observers_) + observer.OnPrefValueChanged(key); } void SegregatedPrefStore::AggregatingObserver::OnInitializationCompleted( @@ -44,9 +44,8 @@ outer_->read_error_delegate_->OnError(read_error); } - FOR_EACH_OBSERVER( - PrefStore::Observer, outer_->observers_, - OnInitializationCompleted(successful_sub_initializations_ == 2)); + for (auto& observer : outer_->observers_) + observer.OnInitializationCompleted(successful_sub_initializations_ == 2); } }
diff --git a/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedService.java b/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedService.java index 02fc45d..7b1c7bb8 100644 --- a/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedService.java +++ b/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedService.java
@@ -22,7 +22,7 @@ public class VariationsSeedService extends IntentService { private static final String TAG = "VariationsSeedServ"; private static final String VARIATIONS_SERVER_URL = - "https://clients4.google.com/chrome-variations/seed?osname=android"; + "https://clientservices.googleapis.com/chrome-variations/seed?osname=android"; private static final int BUFFER_SIZE = 4096; private static final int READ_TIMEOUT = 10000; // time in ms private static final int REQUEST_TIMEOUT = 15000; // time in ms
diff --git a/components/variations/service/variations_service.cc b/components/variations/service/variations_service.cc index 8f5d5df..6b95b26f 100644 --- a/components/variations/service/variations_service.cc +++ b/components/variations/service/variations_service.cc
@@ -626,11 +626,11 @@ DCHECK(thread_checker_.CalledOnValidThread()); if (result.kill_critical_group_change_count > 0) { - FOR_EACH_OBSERVER(Observer, observer_list_, - OnExperimentChangesDetected(Observer::CRITICAL)); + for (auto& observer : observer_list_) + observer.OnExperimentChangesDetected(Observer::CRITICAL); } else if (result.kill_best_effort_group_change_count > 0) { - FOR_EACH_OBSERVER(Observer, observer_list_, - OnExperimentChangesDetected(Observer::BEST_EFFORT)); + for (auto& observer : observer_list_) + observer.OnExperimentChangesDetected(Observer::BEST_EFFORT); } }
diff --git a/components/wallpaper/wallpaper_manager_base.cc b/components/wallpaper/wallpaper_manager_base.cc index e0d38e3..9e2d2331 100644 --- a/components/wallpaper/wallpaper_manager_base.cc +++ b/components/wallpaper/wallpaper_manager_base.cc
@@ -630,7 +630,8 @@ } void WallpaperManagerBase::UpdateWallpaper(bool clear_cache) { - FOR_EACH_OBSERVER(Observer, observers_, OnUpdateWallpaperForTesting()); + for (auto& observer : observers_) + observer.OnUpdateWallpaperForTesting(); if (clear_cache) wallpaper_cache_.clear(); SetUserWallpaperNow(last_selected_user_); @@ -647,8 +648,8 @@ } void WallpaperManagerBase::NotifyAnimationFinished() { - FOR_EACH_OBSERVER(Observer, observers_, - OnWallpaperAnimationFinished(last_selected_user_)); + for (auto& observer : observers_) + observer.OnWallpaperAnimationFinished(last_selected_user_); } // WallpaperManager, protected: -----------------------------------------------
diff --git a/components/wallpaper/wallpaper_resizer.cc b/components/wallpaper/wallpaper_resizer.cc index 93640fd..545a9cc 100644 --- a/components/wallpaper/wallpaper_resizer.cc +++ b/components/wallpaper/wallpaper_resizer.cc
@@ -144,7 +144,8 @@ void WallpaperResizer::OnResizeFinished(SkBitmap* resized_bitmap) { image_ = gfx::ImageSkia::CreateFrom1xBitmap(*resized_bitmap); - FOR_EACH_OBSERVER(WallpaperResizerObserver, observers_, OnWallpaperResized()); + for (auto& observer : observers_) + observer.OnWallpaperResized(); } } // namespace wallpaper
diff --git a/components/zoom/zoom_controller.cc b/components/zoom/zoom_controller.cc index 1fa5f7f..a28a01d 100644 --- a/components/zoom/zoom_controller.cc +++ b/components/zoom/zoom_controller.cc
@@ -127,8 +127,8 @@ ZoomChangedEventData zoom_change_data(web_contents(), old_zoom_level, zoom_level_, zoom_mode_, can_show_bubble); - FOR_EACH_OBSERVER(ZoomObserver, observers_, - OnZoomChanged(zoom_change_data)); + for (auto& observer : observers_) + observer.OnZoomChanged(zoom_change_data); last_client_ = NULL; return true; @@ -221,8 +221,8 @@ } else { // When we don't call any HostZoomMap set functions, we send the event // manually. - FOR_EACH_OBSERVER(ZoomObserver, observers_, - OnZoomChanged(*event_data_)); + for (auto& observer : observers_) + observer.OnZoomChanged(*event_data_); event_data_.reset(); } break; @@ -238,8 +238,8 @@ } else { // When we don't call any HostZoomMap set functions, we send the event // manually. - FOR_EACH_OBSERVER(ZoomObserver, observers_, - OnZoomChanged(*event_data_)); + for (auto& observer : observers_) + observer.OnZoomChanged(*event_data_); event_data_.reset(); } break; @@ -346,8 +346,8 @@ // The zoom bubble should not be shown for zoom changes where the host // is empty. zoom_change_data.can_show_bubble = can_show_bubble_ && !host.empty(); - FOR_EACH_OBSERVER(ZoomObserver, observers_, - OnZoomChanged(zoom_change_data)); + for (auto& observer : observers_) + observer.OnZoomChanged(zoom_change_data); } else { // TODO(wjmaclean) Should we consider having HostZoomMap send both old and // new zoom levels here? @@ -356,8 +356,8 @@ ZoomChangedEventData zoom_change_data(web_contents(), zoom_level, zoom_level, zoom_mode_, false /* can_show_bubble */); - FOR_EACH_OBSERVER(ZoomObserver, observers_, - OnZoomChanged(zoom_change_data)); + for (auto& observer : observers_) + observer.OnZoomChanged(zoom_change_data); } }
diff --git a/components/zoom/zoom_event_manager.cc b/components/zoom/zoom_event_manager.cc index 5c328940..c1fd61c 100644 --- a/components/zoom/zoom_event_manager.cc +++ b/components/zoom/zoom_event_manager.cc
@@ -37,8 +37,8 @@ } void ZoomEventManager::OnDefaultZoomLevelChanged() { - FOR_EACH_OBSERVER(ZoomEventManagerObserver, observers_, - OnDefaultZoomLevelChanged()); + for (auto& observer : observers_) + observer.OnDefaultZoomLevelChanged(); } void ZoomEventManager::AddZoomEventManagerObserver(
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 534bf7f3..00431273 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -848,6 +848,10 @@ "media/media_web_contents_observer.h", "media/midi_host.cc", "media/midi_host.h", + "media/session/audio_focus_delegate.h", + "media/session/audio_focus_delegate_android.cc", + "media/session/audio_focus_delegate_android.h", + "media/session/audio_focus_delegate_default.cc", "media/session/audio_focus_manager.cc", "media/session/audio_focus_manager.h", "media/session/media_metadata_sanitizer.cc", @@ -858,11 +862,7 @@ "media/session/media_session_controller.h", "media/session/media_session_controllers_manager.cc", "media/session/media_session_controllers_manager.h", - "media/session/media_session_delegate.h", - "media/session/media_session_delegate_android.cc", - "media/session/media_session_delegate_android.h", - "media/session/media_session_delegate_default.cc", - "media/session/media_session_observer.h", + "media/session/media_session_player_observer.h", "media/session/media_session_service_impl.cc", "media/session/media_session_service_impl.h", "media/session/media_session_uma_helper.cc", @@ -1717,9 +1717,9 @@ sources -= [ "browser_ipc_logging.cc", "device_sensors/data_fetcher_shared_memory_default.cc", + "media/session/audio_focus_delegate_default.cc", "media/session/audio_focus_manager.cc", "media/session/audio_focus_manager.h", - "media/session/media_session_delegate_default.cc", "power_usage_monitor_impl.cc", "power_usage_monitor_impl.h", "tracing/tracing_ui.cc",
diff --git a/content/browser/android/browser_jni_registrar.cc b/content/browser/android/browser_jni_registrar.cc index 7985f7c..32dfc104 100644 --- a/content/browser/android/browser_jni_registrar.cc +++ b/content/browser/android/browser_jni_registrar.cc
@@ -24,7 +24,7 @@ #include "content/browser/android/web_contents_observer_proxy.h" #include "content/browser/device_sensors/sensor_manager_android.h" #include "content/browser/frame_host/navigation_controller_android.h" -#include "content/browser/media/session/media_session_delegate_android.h" +#include "content/browser/media/session/audio_focus_delegate_android.h" #include "content/browser/memory/memory_monitor_android.h" #include "content/browser/renderer_host/ime_adapter_android.h" #include "content/browser/speech/speech_recognizer_impl_android.h" @@ -35,11 +35,11 @@ namespace { base::android::RegistrationMethod kContentRegisteredMethods[] = { {"AppWebMessagePortService", content::RegisterAppWebMessagePortService}, + {"AudioFocusDelegate", content::AudioFocusDelegateAndroid::Register}, {"BrowserStartupController", content::RegisterBrowserStartupController}, {"ChildProcessLauncher", content::RegisterChildProcessLauncher}, {"ContentVideoView", content::ContentVideoView::RegisterContentVideoView}, {"CoreImpl", mojo::android::RegisterCoreImpl}, - {"MediaSessionDelegate", content::MediaSessionDelegateAndroid::Register}, {"MemoryMonitorAndroid", content::MemoryMonitorAndroid::Register}, {"BackgroundSyncNetworkObserverAndroid", content::BackgroundSyncNetworkObserverAndroid::Observer::
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h index 8d2e243..0e91801 100644 --- a/content/browser/bad_message.h +++ b/content/browser/bad_message.h
@@ -164,7 +164,6 @@ ARH_CREATED_STREAM_WITHOUT_AUTHORIZATION = 140, MDDH_INVALID_DEVICE_TYPE_REQUEST = 141, MDDH_UNAUTHORIZED_ORIGIN = 142, - NMF_INVALID_ID_CLOSE = 143, // Please add new elements here. The naming convention is abbreviated class // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/content/browser/media/session/audio_focus_delegate.h b/content/browser/media/session/audio_focus_delegate.h new file mode 100644 index 0000000..064fa9b --- /dev/null +++ b/content/browser/media/session/audio_focus_delegate.h
@@ -0,0 +1,30 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_MEDIA_SESSION_AUDIO_FOCUS_DELEGATE_H_ +#define CONTENT_BROWSER_MEDIA_SESSION_AUDIO_FOCUS_DELEGATE_H_ + +#include "content/browser/media/session/audio_focus_manager.h" +#include "content/browser/media/session/media_session.h" + +namespace content { + +// AudioFocusDelegate is an interface abstracting audio focus handling for the +// MediaSession class. +class AudioFocusDelegate { + public: + // Factory method returning an implementation of AudioFocusDelegate. + static std::unique_ptr<AudioFocusDelegate> Create( + MediaSession* media_session); + + virtual ~AudioFocusDelegate() = default; + + virtual bool RequestAudioFocus( + AudioFocusManager::AudioFocusType audio_focus_type) = 0; + virtual void AbandonAudioFocus() = 0; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_MEDIA_SESSION_AUDIO_FOCUS_DELEGATE_H_
diff --git a/content/browser/media/session/audio_focus_delegate_android.cc b/content/browser/media/session/audio_focus_delegate_android.cc new file mode 100644 index 0000000..78d1403 --- /dev/null +++ b/content/browser/media/session/audio_focus_delegate_android.cc
@@ -0,0 +1,101 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/media/session/audio_focus_delegate_android.h" + +#include "base/android/context_utils.h" +#include "base/android/jni_android.h" +#include "jni/AudioFocusDelegate_jni.h" + +using base::android::JavaParamRef; + +namespace content { + +// static +bool AudioFocusDelegateAndroid::Register(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +AudioFocusDelegateAndroid::AudioFocusDelegateAndroid( + MediaSession* media_session) + : media_session_(media_session) {} + +AudioFocusDelegateAndroid::~AudioFocusDelegateAndroid() { + JNIEnv* env = base::android::AttachCurrentThread(); + DCHECK(env); + Java_AudioFocusDelegate_tearDown(env, j_media_session_delegate_); +} + +void AudioFocusDelegateAndroid::Initialize() { + JNIEnv* env = base::android::AttachCurrentThread(); + DCHECK(env); + j_media_session_delegate_.Reset(Java_AudioFocusDelegate_create( + env, base::android::GetApplicationContext(), + reinterpret_cast<intptr_t>(this))); +} + +bool AudioFocusDelegateAndroid::RequestAudioFocus( + AudioFocusManager::AudioFocusType audio_focus_type) { + JNIEnv* env = base::android::AttachCurrentThread(); + DCHECK(env); + return Java_AudioFocusDelegate_requestAudioFocus( + env, j_media_session_delegate_, + audio_focus_type == + AudioFocusManager::AudioFocusType::GainTransientMayDuck); +} + +void AudioFocusDelegateAndroid::AbandonAudioFocus() { + JNIEnv* env = base::android::AttachCurrentThread(); + DCHECK(env); + Java_AudioFocusDelegate_abandonAudioFocus(env, j_media_session_delegate_); +} + +void AudioFocusDelegateAndroid::OnSuspend(JNIEnv*, + const JavaParamRef<jobject>&, + jboolean temporary) { + // TODO(mlamouri): this check makes it so that if a MediaSession is paused and + // then loses audio focus, it will still stay in the Suspended state. + // See https://crbug.com/539998 + if (!media_session_->IsActive()) + return; + + if (temporary) { + media_session_->Suspend(MediaSession::SuspendType::SYSTEM); + } else { + media_session_->Stop(MediaSession::SuspendType::SYSTEM); + } +} + +void AudioFocusDelegateAndroid::OnResume(JNIEnv*, + const JavaParamRef<jobject>&) { + if (!media_session_->IsReallySuspended()) + return; + + media_session_->Resume(MediaSession::SuspendType::SYSTEM); +} + +void AudioFocusDelegateAndroid::OnStartDucking(JNIEnv*, jobject) { + media_session_->StartDucking(); +} + +void AudioFocusDelegateAndroid::OnStopDucking(JNIEnv*, jobject) { + media_session_->StopDucking(); +} + +void AudioFocusDelegateAndroid::RecordSessionDuck( + JNIEnv*, + const JavaParamRef<jobject>&) { + media_session_->RecordSessionDuck(); +} + +// static +std::unique_ptr<AudioFocusDelegate> AudioFocusDelegate::Create( + MediaSession* media_session) { + AudioFocusDelegateAndroid* delegate = + new AudioFocusDelegateAndroid(media_session); + delegate->Initialize(); + return std::unique_ptr<AudioFocusDelegate>(delegate); +} + +} // namespace content
diff --git a/content/browser/media/session/media_session_delegate_android.h b/content/browser/media/session/audio_focus_delegate_android.h similarity index 73% rename from content/browser/media/session/media_session_delegate_android.h rename to content/browser/media/session/audio_focus_delegate_android.h index f498dd1..8aafde541 100644 --- a/content/browser/media/session/media_session_delegate_android.h +++ b/content/browser/media/session/audio_focus_delegate_android.h
@@ -2,24 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_DELEGATE_ANDROID_H_ -#define CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_DELEGATE_ANDROID_H_ +#ifndef CONTENT_BROWSER_MEDIA_SESSION_AUDIO_FOCUS_DELEGATE_ANDROID_H_ +#define CONTENT_BROWSER_MEDIA_SESSION_AUDIO_FOCUS_DELEGATE_ANDROID_H_ #include <jni.h> #include "base/android/scoped_java_ref.h" -#include "content/browser/media/session/media_session_delegate.h" +#include "content/browser/media/session/audio_focus_delegate.h" namespace content { -// MediaSessionDelegateAndroid handles the audio focus at a system level on +// AudioFocusDelegateAndroid handles the audio focus at a system level on // Android. It is also proxying the JNI calls. -class MediaSessionDelegateAndroid : public MediaSessionDelegate { +class AudioFocusDelegateAndroid : public AudioFocusDelegate { public: static bool Register(JNIEnv* env); - explicit MediaSessionDelegateAndroid(MediaSession* media_session); - ~MediaSessionDelegateAndroid() override; + explicit AudioFocusDelegateAndroid(MediaSession* media_session); + ~AudioFocusDelegateAndroid() override; void Initialize(); @@ -48,16 +48,16 @@ // Record when the Android system requests the MediaSession to duck. // Called by Java through JNI. void RecordSessionDuck(JNIEnv* env, - const base::android::JavaParamRef<jobject> &obj); + const base::android::JavaParamRef<jobject>& obj); private: // Weak pointer because |this| is owned by |media_session_|. MediaSession* media_session_; base::android::ScopedJavaGlobalRef<jobject> j_media_session_delegate_; - DISALLOW_COPY_AND_ASSIGN(MediaSessionDelegateAndroid); + DISALLOW_COPY_AND_ASSIGN(AudioFocusDelegateAndroid); }; } // namespace content -#endif // CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_DELEGATE_ANDROID_H_ +#endif // CONTENT_BROWSER_MEDIA_SESSION_AUDIO_FOCUS_DELEGATE_ANDROID_H_
diff --git a/content/browser/media/session/media_session_delegate_android_browsertest.cc b/content/browser/media/session/audio_focus_delegate_android_browsertest.cc similarity index 80% rename from content/browser/media/session/media_session_delegate_android_browsertest.cc rename to content/browser/media/session/audio_focus_delegate_android_browsertest.cc index e2d00a3..efdb3feb 100644 --- a/content/browser/media/session/media_session_delegate_android_browsertest.cc +++ b/content/browser/media/session/audio_focus_delegate_android_browsertest.cc
@@ -10,14 +10,14 @@ #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "content/browser/media/session/media_session.h" -#include "content/browser/media/session/mock_media_session_observer.h" +#include "content/browser/media/session/mock_media_session_player_observer.h" #include "content/public/test/content_browser_test.h" #include "content/shell/browser/shell.h" #include "media/base/media_content_type.h" namespace content { -class MediaSessionDelegateAndroidBrowserTest : public ContentBrowserTest {}; +class AudioFocusDelegateAndroidBrowserTest : public ContentBrowserTest {}; // MAYBE_OnAudioFocusChangeAfterDtorCrash will hit a DCHECK before the crash, it // is the only way found to actually reproduce the crash so as a result, the @@ -31,10 +31,10 @@ DISABLED_OnAudioFocusChangeAfterDtorCrash #endif -IN_PROC_BROWSER_TEST_F(MediaSessionDelegateAndroidBrowserTest, +IN_PROC_BROWSER_TEST_F(AudioFocusDelegateAndroidBrowserTest, MAYBE_OnAudioFocusChangeAfterDtorCrash) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); MediaSession* media_session = MediaSession::Get(shell()->web_contents()); ASSERT_TRUE(media_session); @@ -43,14 +43,14 @@ MediaSession* other_media_session = MediaSession::Get(other_web_contents); ASSERT_TRUE(other_media_session); - media_session_observer->StartNewPlayer(); - media_session->AddPlayer(media_session_observer.get(), 0, + player_observer->StartNewPlayer(); + media_session->AddPlayer(player_observer.get(), 0, media::MediaContentType::Persistent); EXPECT_TRUE(media_session->IsActive()); EXPECT_FALSE(other_media_session->IsActive()); - media_session_observer->StartNewPlayer(); - other_media_session->AddPlayer(media_session_observer.get(), 1, + player_observer->StartNewPlayer(); + other_media_session->AddPlayer(player_observer.get(), 1, media::MediaContentType::Persistent); EXPECT_TRUE(media_session->IsActive()); EXPECT_TRUE(other_media_session->IsActive());
diff --git a/content/browser/media/session/audio_focus_delegate_default.cc b/content/browser/media/session/audio_focus_delegate_default.cc new file mode 100644 index 0000000..f36887f --- /dev/null +++ b/content/browser/media/session/audio_focus_delegate_default.cc
@@ -0,0 +1,65 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/media/session/audio_focus_delegate.h" + +#include "base/command_line.h" +#include "content/browser/media/session/audio_focus_manager.h" +#include "media/base/media_switches.h" + +namespace content { + +using AudioFocusType = AudioFocusManager::AudioFocusType; + +namespace { + +// AudioFocusDelegateDefault is the default implementation of +// AudioFocusDelegate which only handles audio focus between WebContents. +class AudioFocusDelegateDefault : public AudioFocusDelegate { + public: + explicit AudioFocusDelegateDefault(MediaSession* media_session); + ~AudioFocusDelegateDefault() override; + + // AudioFocusDelegate implementation. + bool RequestAudioFocus( + AudioFocusManager::AudioFocusType audio_focus_type) override; + void AbandonAudioFocus() override; + + private: + // Weak pointer because |this| is owned by |media_session_|. + MediaSession* media_session_; +}; + +} // anonymous namespace + +AudioFocusDelegateDefault::AudioFocusDelegateDefault( + MediaSession* media_session) + : media_session_(media_session) {} + +AudioFocusDelegateDefault::~AudioFocusDelegateDefault() = default; + +bool AudioFocusDelegateDefault::RequestAudioFocus( + AudioFocusManager::AudioFocusType audio_focus_type) { + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableDefaultMediaSession)) { + return true; + } + + AudioFocusManager::GetInstance()->RequestAudioFocus(media_session_, + audio_focus_type); + return true; +} + +void AudioFocusDelegateDefault::AbandonAudioFocus() { + AudioFocusManager::GetInstance()->AbandonAudioFocus(media_session_); +} + +// static +std::unique_ptr<AudioFocusDelegate> AudioFocusDelegate::Create( + MediaSession* media_session) { + return std::unique_ptr<AudioFocusDelegate>( + new AudioFocusDelegateDefault(media_session)); +} + +} // namespace content
diff --git a/content/browser/media/session/media_session_delegate_default_browsertest.cc b/content/browser/media/session/audio_focus_delegate_default_browsertest.cc similarity index 84% rename from content/browser/media/session/media_session_delegate_default_browsertest.cc rename to content/browser/media/session/audio_focus_delegate_default_browsertest.cc index 921c084..8456748e 100644 --- a/content/browser/media/session/media_session_delegate_default_browsertest.cc +++ b/content/browser/media/session/audio_focus_delegate_default_browsertest.cc
@@ -4,7 +4,7 @@ #include "base/command_line.h" #include "content/browser/media/session/media_session.h" -#include "content/browser/media/session/mock_media_session_observer.h" +#include "content/browser/media/session/mock_media_session_player_observer.h" #include "content/public/test/content_browser_test.h" #include "content/shell/browser/shell.h" #include "media/base/media_content_type.h" @@ -19,8 +19,8 @@ } void Run(WebContents* start_contents, WebContents* interrupt_contents) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> + player_observer(new MockMediaSessionPlayerObserver); MediaSession* media_session = MediaSession::Get(start_contents); ASSERT_TRUE(media_session); @@ -28,14 +28,14 @@ MediaSession* other_media_session = MediaSession::Get(interrupt_contents); ASSERT_TRUE(other_media_session); - media_session_observer->StartNewPlayer(); - media_session->AddPlayer(media_session_observer.get(), 0, + player_observer->StartNewPlayer(); + media_session->AddPlayer(player_observer.get(), 0, media::MediaContentType::Persistent); EXPECT_TRUE(media_session->IsActive()); EXPECT_FALSE(other_media_session->IsActive()); - media_session_observer->StartNewPlayer(); - other_media_session->AddPlayer(media_session_observer.get(), 1, + player_observer->StartNewPlayer(); + other_media_session->AddPlayer(player_observer.get(), 1, media::MediaContentType::Persistent); EXPECT_FALSE(media_session->IsActive()); EXPECT_TRUE(other_media_session->IsActive());
diff --git a/content/browser/media/session/audio_focus_manager_unittest.cc b/content/browser/media/session/audio_focus_manager_unittest.cc index df66e86..d853a2cb 100644 --- a/content/browser/media/session/audio_focus_manager_unittest.cc +++ b/content/browser/media/session/audio_focus_manager_unittest.cc
@@ -6,7 +6,7 @@ #include "base/command_line.h" #include "content/browser/media/session/media_session.h" -#include "content/browser/media/session/media_session_observer.h" +#include "content/browser/media/session/media_session_player_observer.h" #include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_browser_context.h" #include "content/public/test/test_browser_thread.h" @@ -18,7 +18,7 @@ namespace { -class MockMediaSessionObserver : public MediaSessionObserver { +class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver { public: void OnSuspend(int player_id) override {} void OnResume(int player_id) override {} @@ -41,7 +41,7 @@ rph_factory_.reset(new MockRenderProcessHostFactory()); SiteInstanceImpl::set_render_process_host_factory(rph_factory_.get()); browser_context_.reset(new TestBrowserContext()); - pepper_observer_.reset(new MockMediaSessionObserver()); + pepper_observer_.reset(new MockMediaSessionPlayerObserver()); } void TearDown() override { @@ -97,7 +97,7 @@ SiteInstance::SiteInstance::Create(browser_context_.get())); } - std::unique_ptr<MediaSessionObserver> pepper_observer_; + std::unique_ptr<MediaSessionPlayerObserver> pepper_observer_; private: base::MessageLoopForUI message_loop_;
diff --git a/content/browser/media/session/media_session.cc b/content/browser/media/session/media_session.cc index aa165b9..f4d3c09 100644 --- a/content/browser/media/session/media_session.cc +++ b/content/browser/media/session/media_session.cc
@@ -4,8 +4,8 @@ #include "content/browser/media/session/media_session.h" -#include "content/browser/media/session/media_session_delegate.h" -#include "content/browser/media/session/media_session_observer.h" +#include "content/browser/media/session/audio_focus_delegate.h" +#include "content/browser/media/session/media_session_player_observer.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_delegate.h" @@ -25,11 +25,10 @@ DEFINE_WEB_CONTENTS_USER_DATA_KEY(MediaSession); -MediaSession::PlayerIdentifier::PlayerIdentifier(MediaSessionObserver* observer, - int player_id) - : observer(observer), - player_id(player_id) { -} +MediaSession::PlayerIdentifier::PlayerIdentifier( + MediaSessionPlayerObserver* observer, + int player_id) + : observer(observer), player_id(player_id) {} bool MediaSession::PlayerIdentifier::operator==( const PlayerIdentifier& other) const { @@ -38,7 +37,7 @@ size_t MediaSession::PlayerIdentifier::Hash::operator()( const PlayerIdentifier& player_identifier) const { - size_t hash = BASE_HASH_NAMESPACE::hash<MediaSessionObserver*>()( + size_t hash = BASE_HASH_NAMESPACE::hash<MediaSessionPlayerObserver*>()( player_identifier.observer); hash += BASE_HASH_NAMESPACE::hash<int>()(player_identifier.player_id); return hash; @@ -79,7 +78,7 @@ ->OnMediaSessionMetadataChanged(); } -bool MediaSession::AddPlayer(MediaSessionObserver* observer, +bool MediaSession::AddPlayer(MediaSessionPlayerObserver* observer, int player_id, media::MediaContentType media_content_type) { if (media_content_type == media::MediaContentType::Pepper) @@ -126,7 +125,7 @@ return true; } -void MediaSession::RemovePlayer(MediaSessionObserver* observer, +void MediaSession::RemovePlayer(MediaSessionPlayerObserver* observer, int player_id) { auto it = players_.find(PlayerIdentifier(observer, player_id)); if (it != players_.end()) @@ -139,7 +138,7 @@ AbandonSystemAudioFocusIfNeeded(); } -void MediaSession::RemovePlayers(MediaSessionObserver* observer) { +void MediaSession::RemovePlayers(MediaSessionPlayerObserver* observer) { for (auto it = players_.begin(); it != players_.end(); ) { if (it->observer == observer) players_.erase(it++); @@ -162,7 +161,7 @@ MediaSessionSuspendedSource::SystemTransientDuck); } -void MediaSession::OnPlayerPaused(MediaSessionObserver* observer, +void MediaSession::OnPlayerPaused(MediaSessionPlayerObserver* observer, int player_id) { // If a playback is completed, BrowserMediaPlayerManager will call // OnPlayerPaused() after RemovePlayer(). This is a workaround. @@ -285,7 +284,7 @@ } void MediaSession::SetDelegateForTests( - std::unique_ptr<MediaSessionDelegate> delegate) { + std::unique_ptr<AudioFocusDelegate> delegate) { delegate_ = std::move(delegate); } @@ -377,7 +376,7 @@ is_ducking_(false) {} void MediaSession::Initialize() { - delegate_ = MediaSessionDelegate::Create(this); + delegate_ = AudioFocusDelegate::Create(this); } bool MediaSession::RequestSystemAudioFocus( @@ -426,7 +425,7 @@ } } -bool MediaSession::AddPepperPlayer(MediaSessionObserver* observer, +bool MediaSession::AddPepperPlayer(MediaSessionPlayerObserver* observer, int player_id) { bool success = RequestSystemAudioFocus( AudioFocusManager::AudioFocusType::Gain);
diff --git a/content/browser/media/session/media_session.h b/content/browser/media/session/media_session.h index ba8f999..9239c785 100644 --- a/content/browser/media/session/media_session.h +++ b/content/browser/media/session/media_session.h
@@ -26,9 +26,9 @@ namespace content { +class AudioFocusDelegate; class AudioFocusManagerTest; -class MediaSessionDelegate; -class MediaSessionObserver; +class MediaSessionPlayerObserver; class MediaSessionStateObserver; class MediaSessionVisibilityBrowserTest; @@ -77,18 +77,18 @@ // Adds the given player to the current media session. Returns whether the // player was successfully added. If it returns false, AddPlayer() should be // called again later. - CONTENT_EXPORT bool AddPlayer(MediaSessionObserver* observer, + CONTENT_EXPORT bool AddPlayer(MediaSessionPlayerObserver* observer, int player_id, media::MediaContentType media_content_type); // Removes the given player from the current media session. Abandons audio // focus if that was the last player in the session. - CONTENT_EXPORT void RemovePlayer(MediaSessionObserver* observer, + CONTENT_EXPORT void RemovePlayer(MediaSessionPlayerObserver* observer, int player_id); // Removes all the players associated with |observer|. Abandons audio focus if // these were the last players in the session. - CONTENT_EXPORT void RemovePlayers(MediaSessionObserver* observer); + CONTENT_EXPORT void RemovePlayers(MediaSessionPlayerObserver* observer); // Record that the session was ducked. void RecordSessionDuck(); @@ -96,7 +96,7 @@ // Called when a player is paused in the content. // If the paused player is the last player, we suspend the MediaSession. // Otherwise, the paused player will be removed from the MediaSession. - CONTENT_EXPORT void OnPlayerPaused(MediaSessionObserver* observer, + CONTENT_EXPORT void OnPlayerPaused(MediaSessionPlayerObserver* observer, int player_id); // Resume the media session. @@ -156,14 +156,14 @@ friend class content::MediaSessionStateObserver; CONTENT_EXPORT void SetDelegateForTests( - std::unique_ptr<MediaSessionDelegate> delegate); + std::unique_ptr<AudioFocusDelegate> delegate); CONTENT_EXPORT bool IsActiveForTest() const; CONTENT_EXPORT void RemoveAllPlayersForTest(); CONTENT_EXPORT MediaSessionUmaHelper* uma_helper_for_test(); // Representation of a player for the MediaSession. struct PlayerIdentifier { - PlayerIdentifier(MediaSessionObserver* observer, int player_id); + PlayerIdentifier(MediaSessionPlayerObserver* observer, int player_id); PlayerIdentifier(const PlayerIdentifier&) = default; void operator=(const PlayerIdentifier&) = delete; @@ -174,7 +174,7 @@ size_t operator()(const PlayerIdentifier& player_identifier) const; }; - MediaSessionObserver* observer; + MediaSessionPlayerObserver* observer; int player_id; }; using PlayersMap = base::hash_set<PlayerIdentifier, PlayerIdentifier::Hash>; @@ -188,7 +188,7 @@ State new_state); CONTENT_EXPORT void OnResumeInternal(SuspendType suspend_type); - // Requests audio focus to the MediaSessionDelegate. + // Requests audio focus to the AudioFocusDelegate. // Returns whether the request was granted. CONTENT_EXPORT bool RequestSystemAudioFocus( AudioFocusManager::AudioFocusType audio_focus_type); @@ -216,10 +216,10 @@ RegisterMediaSessionStateChangedCallbackForTest( const StateChangedCallback& cb); - CONTENT_EXPORT bool AddPepperPlayer(MediaSessionObserver* observer, + CONTENT_EXPORT bool AddPepperPlayer(MediaSessionPlayerObserver* observer, int player_id); - std::unique_ptr<MediaSessionDelegate> delegate_; + std::unique_ptr<AudioFocusDelegate> delegate_; PlayersMap players_; PlayersMap pepper_players_;
diff --git a/content/browser/media/session/media_session_browsertest.cc b/content/browser/media/session/media_session_browsertest.cc index 959bead1..5f79eeb 100644 --- a/content/browser/media/session/media_session_browsertest.cc +++ b/content/browser/media/session/media_session_browsertest.cc
@@ -13,8 +13,8 @@ #include "base/metrics/histogram_samples.h" #include "base/test/histogram_tester.h" #include "base/test/simple_test_tick_clock.h" -#include "content/browser/media/session/media_session_delegate.h" -#include "content/browser/media/session/mock_media_session_observer.h" +#include "content/browser/media/session/audio_focus_delegate.h" +#include "content/browser/media/session/mock_media_session_player_observer.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/test/content_browser_test.h" @@ -25,10 +25,10 @@ using content::WebContents; using content::WebContentsObserver; using content::MediaSession; -using content::MediaSessionDelegate; -using content::MediaSessionObserver; +using content::AudioFocusDelegate; +using content::MediaSessionPlayerObserver; using content::MediaSessionUmaHelper; -using content::MockMediaSessionObserver; +using content::MockMediaSessionPlayerObserver; using ::testing::Expectation; @@ -37,7 +37,7 @@ const double kDefaultVolumeMultiplier = 1.0; const double kDuckingVolumeMultiplier = 0.2; -class MockMediaSessionDelegate : public MediaSessionDelegate { +class MockAudioFocusDelegate : public AudioFocusDelegate { public: bool RequestAudioFocus(content::AudioFocusManager::AudioFocusType) override { return true; @@ -69,7 +69,7 @@ new MockWebContentsObserver(shell()->web_contents())); media_session_ = MediaSession::Get(shell()->web_contents()); media_session_->SetDelegateForTests( - std::unique_ptr<MediaSessionDelegate>(new MockMediaSessionDelegate())); + std::unique_ptr<AudioFocusDelegate>(new MockAudioFocusDelegate())); ASSERT_TRUE(media_session_); } @@ -82,32 +82,37 @@ ContentBrowserTest::TearDownOnMainThread(); } - void StartNewPlayer(MockMediaSessionObserver* media_session_observer, - media::MediaContentType media_content_type) { - bool result = - AddPlayer(media_session_observer, - media_session_observer->StartNewPlayer(), media_content_type); + void StartNewPlayer( + MockMediaSessionPlayerObserver* player_observer, + media::MediaContentType media_content_type) { + bool result = AddPlayer(player_observer, + player_observer->StartNewPlayer(), + media_content_type); EXPECT_TRUE(result); } - bool AddPlayer(MockMediaSessionObserver* media_session_observer, + bool AddPlayer(MockMediaSessionPlayerObserver* player_observer, int player_id, media::MediaContentType type) { - return media_session_->AddPlayer(media_session_observer, player_id, type); + return media_session_->AddPlayer(player_observer, player_id, + type); } - void RemovePlayer(MockMediaSessionObserver* media_session_observer, - int player_id) { - media_session_->RemovePlayer(media_session_observer, player_id); + void RemovePlayer( + MockMediaSessionPlayerObserver* player_observer, + int player_id) { + media_session_->RemovePlayer(player_observer, player_id); } - void RemovePlayers(MockMediaSessionObserver* media_session_observer) { - media_session_->RemovePlayers(media_session_observer); + void RemovePlayers( + MockMediaSessionPlayerObserver* player_observer) { + media_session_->RemovePlayers(player_observer); } - void OnPlayerPaused(MockMediaSessionObserver* media_session_observer, - int player_id) { - media_session_->OnPlayerPaused(media_session_observer, player_id); + void OnPlayerPaused( + MockMediaSessionPlayerObserver* player_observer, + int player_id) { + media_session_->OnPlayerPaused(player_observer, player_id); } bool HasAudioFocus() { return media_session_->IsActiveForTest(); } @@ -168,171 +173,170 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, PlayersFromSameObserverDoNotStopEachOtherInSameSession) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - EXPECT_TRUE(media_session_observer->IsPlaying(0)); - EXPECT_TRUE(media_session_observer->IsPlaying(1)); - EXPECT_TRUE(media_session_observer->IsPlaying(2)); + EXPECT_TRUE(player_observer->IsPlaying(0)); + EXPECT_TRUE(player_observer->IsPlaying(1)); + EXPECT_TRUE(player_observer->IsPlaying(2)); } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, PlayersFromManyObserverDoNotStopEachOtherInSameSession) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer_1( - new MockMediaSessionObserver); - std::unique_ptr<MockMediaSessionObserver> media_session_observer_2( - new MockMediaSessionObserver); - std::unique_ptr<MockMediaSessionObserver> media_session_observer_3( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> + player_observer_1(new MockMediaSessionPlayerObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> + player_observer_2(new MockMediaSessionPlayerObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> + player_observer_3(new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer_1.get(), + StartNewPlayer(player_observer_1.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer_2.get(), + StartNewPlayer(player_observer_2.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer_3.get(), + StartNewPlayer(player_observer_3.get(), media::MediaContentType::Persistent); - EXPECT_TRUE(media_session_observer_1->IsPlaying(0)); - EXPECT_TRUE(media_session_observer_2->IsPlaying(0)); - EXPECT_TRUE(media_session_observer_3->IsPlaying(0)); + EXPECT_TRUE(player_observer_1->IsPlaying(0)); + EXPECT_TRUE(player_observer_2->IsPlaying(0)); + EXPECT_TRUE(player_observer_3->IsPlaying(0)); } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, SuspendedMediaSessionStopsPlayers) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); SystemSuspend(true); - EXPECT_FALSE(media_session_observer->IsPlaying(0)); - EXPECT_FALSE(media_session_observer->IsPlaying(1)); - EXPECT_FALSE(media_session_observer->IsPlaying(2)); + EXPECT_FALSE(player_observer->IsPlaying(0)); + EXPECT_FALSE(player_observer->IsPlaying(1)); + EXPECT_FALSE(player_observer->IsPlaying(2)); } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ResumedMediaSessionRestartsPlayers) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); SystemSuspend(true); SystemResume(); - EXPECT_TRUE(media_session_observer->IsPlaying(0)); - EXPECT_TRUE(media_session_observer->IsPlaying(1)); - EXPECT_TRUE(media_session_observer->IsPlaying(2)); + EXPECT_TRUE(player_observer->IsPlaying(0)); + EXPECT_TRUE(player_observer->IsPlaying(1)); + EXPECT_TRUE(player_observer->IsPlaying(2)); } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, StartedPlayerOnSuspendedSessionPlaysAlone) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - EXPECT_TRUE(media_session_observer->IsPlaying(0)); + EXPECT_TRUE(player_observer->IsPlaying(0)); SystemSuspend(true); - EXPECT_FALSE(media_session_observer->IsPlaying(0)); + EXPECT_FALSE(player_observer->IsPlaying(0)); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - EXPECT_FALSE(media_session_observer->IsPlaying(0)); - EXPECT_TRUE(media_session_observer->IsPlaying(1)); + EXPECT_FALSE(player_observer->IsPlaying(0)); + EXPECT_TRUE(player_observer->IsPlaying(1)); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - EXPECT_FALSE(media_session_observer->IsPlaying(0)); - EXPECT_TRUE(media_session_observer->IsPlaying(1)); - EXPECT_TRUE(media_session_observer->IsPlaying(2)); + EXPECT_FALSE(player_observer->IsPlaying(0)); + EXPECT_TRUE(player_observer->IsPlaying(1)); + EXPECT_TRUE(player_observer->IsPlaying(2)); } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, InitialVolumeMultiplier) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); EXPECT_EQ(kDefaultVolumeMultiplier, - media_session_observer->GetVolumeMultiplier(0)); + player_observer->GetVolumeMultiplier(0)); EXPECT_EQ(kDefaultVolumeMultiplier, - media_session_observer->GetVolumeMultiplier(1)); - + player_observer->GetVolumeMultiplier(1)); } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, StartDuckingReducesVolumeMultiplier) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); SystemStartDucking(); EXPECT_EQ(kDuckingVolumeMultiplier, - media_session_observer->GetVolumeMultiplier(0)); + player_observer->GetVolumeMultiplier(0)); EXPECT_EQ(kDuckingVolumeMultiplier, - media_session_observer->GetVolumeMultiplier(1)); + player_observer->GetVolumeMultiplier(1)); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); EXPECT_EQ(kDuckingVolumeMultiplier, - media_session_observer->GetVolumeMultiplier(2)); + player_observer->GetVolumeMultiplier(2)); } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, StopDuckingRecoversVolumeMultiplier) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); SystemStartDucking(); SystemStopDucking(); EXPECT_EQ(kDefaultVolumeMultiplier, - media_session_observer->GetVolumeMultiplier(0)); + player_observer->GetVolumeMultiplier(0)); EXPECT_EQ(kDefaultVolumeMultiplier, - media_session_observer->GetVolumeMultiplier(1)); + player_observer->GetVolumeMultiplier(1)); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); EXPECT_EQ(kDefaultVolumeMultiplier, - media_session_observer->GetVolumeMultiplier(2)); + player_observer->GetVolumeMultiplier(2)); } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, AudioFocusInitialState) { @@ -340,20 +344,20 @@ } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, StartPlayerGivesFocus) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); EXPECT_TRUE(HasAudioFocus()); } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, SuspendGivesAwayAudioFocus) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); SystemSuspend(true); @@ -362,10 +366,10 @@ } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, StopGivesAwayAudioFocus) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); media_session_->Stop(MediaSession::SuspendType::UI); @@ -374,10 +378,10 @@ } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ResumeGivesBackAudioFocus) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); SystemSuspend(true); @@ -388,198 +392,198 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, RemovingLastPlayerDropsAudioFocus) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - RemovePlayer(media_session_observer.get(), 0); + RemovePlayer(player_observer.get(), 0); EXPECT_TRUE(HasAudioFocus()); - RemovePlayer(media_session_observer.get(), 1); + RemovePlayer(player_observer.get(), 1); EXPECT_TRUE(HasAudioFocus()); - RemovePlayer(media_session_observer.get(), 2); + RemovePlayer(player_observer.get(), 2); EXPECT_FALSE(HasAudioFocus()); } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, RemovingLastPlayerFromManyObserversDropsAudioFocus) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer_1( - new MockMediaSessionObserver); - std::unique_ptr<MockMediaSessionObserver> media_session_observer_2( - new MockMediaSessionObserver); - std::unique_ptr<MockMediaSessionObserver> media_session_observer_3( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> + player_observer_1(new MockMediaSessionPlayerObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> + player_observer_2(new MockMediaSessionPlayerObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> + player_observer_3(new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer_1.get(), + StartNewPlayer(player_observer_1.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer_2.get(), + StartNewPlayer(player_observer_2.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer_3.get(), + StartNewPlayer(player_observer_3.get(), media::MediaContentType::Persistent); - RemovePlayer(media_session_observer_1.get(), 0); + RemovePlayer(player_observer_1.get(), 0); EXPECT_TRUE(HasAudioFocus()); - RemovePlayer(media_session_observer_2.get(), 0); + RemovePlayer(player_observer_2.get(), 0); EXPECT_TRUE(HasAudioFocus()); - RemovePlayer(media_session_observer_3.get(), 0); + RemovePlayer(player_observer_3.get(), 0); EXPECT_FALSE(HasAudioFocus()); } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, RemovingAllPlayersFromObserversDropsAudioFocus) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer_1( - new MockMediaSessionObserver); - std::unique_ptr<MockMediaSessionObserver> media_session_observer_2( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> + player_observer_1(new MockMediaSessionPlayerObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> + player_observer_2(new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer_1.get(), + StartNewPlayer(player_observer_1.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer_1.get(), + StartNewPlayer(player_observer_1.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer_2.get(), + StartNewPlayer(player_observer_2.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer_2.get(), + StartNewPlayer(player_observer_2.get(), media::MediaContentType::Persistent); - RemovePlayers(media_session_observer_1.get()); + RemovePlayers(player_observer_1.get()); EXPECT_TRUE(HasAudioFocus()); - RemovePlayers(media_session_observer_2.get()); + RemovePlayers(player_observer_2.get()); EXPECT_FALSE(HasAudioFocus()); } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ResumePlayGivesAudioFocus) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - RemovePlayer(media_session_observer.get(), 0); + RemovePlayer(player_observer.get(), 0); EXPECT_FALSE(HasAudioFocus()); - EXPECT_TRUE(AddPlayer(media_session_observer.get(), 0, + EXPECT_TRUE(AddPlayer(player_observer.get(), 0, media::MediaContentType::Persistent)); EXPECT_TRUE(HasAudioFocus()); } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ResumeSuspendAreSentOnlyOncePerPlayers) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - EXPECT_EQ(0, media_session_observer->received_suspend_calls()); - EXPECT_EQ(0, media_session_observer->received_resume_calls()); + EXPECT_EQ(0, player_observer->received_suspend_calls()); + EXPECT_EQ(0, player_observer->received_resume_calls()); SystemSuspend(true); - EXPECT_EQ(3, media_session_observer->received_suspend_calls()); + EXPECT_EQ(3, player_observer->received_suspend_calls()); SystemResume(); - EXPECT_EQ(3, media_session_observer->received_resume_calls()); + EXPECT_EQ(3, player_observer->received_resume_calls()); } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ResumeSuspendAreSentOnlyOncePerPlayersAddedTwice) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); // Adding the three players above again. - EXPECT_TRUE(AddPlayer(media_session_observer.get(), 0, + EXPECT_TRUE(AddPlayer(player_observer.get(), 0, media::MediaContentType::Persistent)); - EXPECT_TRUE(AddPlayer(media_session_observer.get(), 1, + EXPECT_TRUE(AddPlayer(player_observer.get(), 1, media::MediaContentType::Persistent)); - EXPECT_TRUE(AddPlayer(media_session_observer.get(), 2, + EXPECT_TRUE(AddPlayer(player_observer.get(), 2, media::MediaContentType::Persistent)); - EXPECT_EQ(0, media_session_observer->received_suspend_calls()); - EXPECT_EQ(0, media_session_observer->received_resume_calls()); + EXPECT_EQ(0, player_observer->received_suspend_calls()); + EXPECT_EQ(0, player_observer->received_resume_calls()); SystemSuspend(true); - EXPECT_EQ(3, media_session_observer->received_suspend_calls()); + EXPECT_EQ(3, player_observer->received_suspend_calls()); SystemResume(); - EXPECT_EQ(3, media_session_observer->received_resume_calls()); + EXPECT_EQ(3, player_observer->received_resume_calls()); } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, RemovingTheSamePlayerTwiceIsANoop) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - RemovePlayer(media_session_observer.get(), 0); - RemovePlayer(media_session_observer.get(), 0); + RemovePlayer(player_observer.get(), 0); + RemovePlayer(player_observer.get(), 0); } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, AudioFocusType) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); // Starting a player with a given type should set the session to that type. - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); EXPECT_EQ(content::AudioFocusManager::AudioFocusType::GainTransientMayDuck, GetSessionAudioFocusType()); // Adding a player of the same type should have no effect on the type. - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); EXPECT_EQ(content::AudioFocusManager::AudioFocusType::GainTransientMayDuck, GetSessionAudioFocusType()); // Adding a player of Content type should override the current type. - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); EXPECT_EQ(content::AudioFocusManager::AudioFocusType::Gain, GetSessionAudioFocusType()); // Adding a player of the Transient type should have no effect on the type. - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); EXPECT_EQ(content::AudioFocusManager::AudioFocusType::Gain, GetSessionAudioFocusType()); - EXPECT_TRUE(media_session_observer->IsPlaying(0)); - EXPECT_TRUE(media_session_observer->IsPlaying(1)); - EXPECT_TRUE(media_session_observer->IsPlaying(2)); - EXPECT_TRUE(media_session_observer->IsPlaying(3)); + EXPECT_TRUE(player_observer->IsPlaying(0)); + EXPECT_TRUE(player_observer->IsPlaying(1)); + EXPECT_TRUE(player_observer->IsPlaying(2)); + EXPECT_TRUE(player_observer->IsPlaying(3)); SystemSuspend(true); - EXPECT_FALSE(media_session_observer->IsPlaying(0)); - EXPECT_FALSE(media_session_observer->IsPlaying(1)); - EXPECT_FALSE(media_session_observer->IsPlaying(2)); - EXPECT_FALSE(media_session_observer->IsPlaying(3)); + EXPECT_FALSE(player_observer->IsPlaying(0)); + EXPECT_FALSE(player_observer->IsPlaying(1)); + EXPECT_FALSE(player_observer->IsPlaying(2)); + EXPECT_FALSE(player_observer->IsPlaying(3)); EXPECT_EQ(content::AudioFocusManager::AudioFocusType::Gain, GetSessionAudioFocusType()); SystemResume(); - EXPECT_TRUE(media_session_observer->IsPlaying(0)); - EXPECT_TRUE(media_session_observer->IsPlaying(1)); - EXPECT_TRUE(media_session_observer->IsPlaying(2)); - EXPECT_TRUE(media_session_observer->IsPlaying(3)); + EXPECT_TRUE(player_observer->IsPlaying(0)); + EXPECT_TRUE(player_observer->IsPlaying(1)); + EXPECT_TRUE(player_observer->IsPlaying(2)); + EXPECT_TRUE(player_observer->IsPlaying(3)); EXPECT_EQ(content::AudioFocusManager::AudioFocusType::Gain, GetSessionAudioFocusType()); @@ -589,11 +593,11 @@ EXPECT_CALL(*mock_web_contents_observer(), MediaSessionStateChanged(true, false)); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); // Starting a player with a content type should show the media controls. - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); EXPECT_TRUE(IsControllable()); @@ -604,11 +608,11 @@ EXPECT_CALL(*mock_web_contents_observer(), MediaSessionStateChanged(false, false)); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); // Starting a player with a transient type should not show the media controls. - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); EXPECT_FALSE(IsControllable()); @@ -622,13 +626,13 @@ MediaSessionStateChanged(false, true)) .After(showControls); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - RemovePlayers(media_session_observer.get()); + RemovePlayers(player_observer.get()); EXPECT_FALSE(IsControllable()); EXPECT_TRUE(IsSuspended()); @@ -638,14 +642,14 @@ EXPECT_CALL(*mock_web_contents_observer(), MediaSessionStateChanged(true, false)); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); // Transient player join the session without affecting the controls. - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); EXPECT_TRUE(IsControllable()); @@ -660,14 +664,14 @@ MediaSessionStateChanged(true, false)) .After(dontShowControls); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); // The controls are shown when the content player is added. - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); EXPECT_TRUE(IsControllable()); @@ -679,17 +683,17 @@ EXPECT_CALL(*mock_web_contents_observer(), MediaSessionStateChanged(true, false)); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); // Removing only content player doesn't hide the controls since the session // is still active. - RemovePlayer(media_session_observer.get(), 0); + RemovePlayer(player_observer.get(), 0); EXPECT_TRUE(IsControllable()); EXPECT_FALSE(IsSuspended()); @@ -702,20 +706,20 @@ EXPECT_CALL(*mock_web_contents_observer(), MediaSessionStateChanged(false, true)) .After(showControls); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - RemovePlayer(media_session_observer.get(), 0); + RemovePlayer(player_observer.get(), 0); EXPECT_TRUE(IsControllable()); EXPECT_FALSE(IsSuspended()); - RemovePlayer(media_session_observer.get(), 1); + RemovePlayer(player_observer.get(), 1); EXPECT_FALSE(IsControllable()); EXPECT_TRUE(IsSuspended()); @@ -729,15 +733,15 @@ MediaSessionStateChanged(false, true)) .After(showControls); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - RemovePlayers(media_session_observer.get()); + RemovePlayers(player_observer.get()); EXPECT_FALSE(IsControllable()); EXPECT_TRUE(IsSuspended()); @@ -751,20 +755,20 @@ MediaSessionStateChanged(true, true)) .After(showControls); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); - OnPlayerPaused(media_session_observer.get(), 0); + OnPlayerPaused(player_observer.get(), 0); EXPECT_TRUE(IsControllable()); EXPECT_FALSE(IsSuspended()); - OnPlayerPaused(media_session_observer.get(), 1); + OnPlayerPaused(player_observer.get(), 1); EXPECT_TRUE(IsControllable()); EXPECT_TRUE(IsSuspended()); @@ -778,10 +782,10 @@ MediaSessionStateChanged(true, true)) .After(showControls); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); SystemSuspend(true); @@ -800,10 +804,10 @@ MediaSessionStateChanged(true, false)) .After(pauseControls); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); SystemSuspend(true); SystemResume(); @@ -820,10 +824,10 @@ MediaSessionStateChanged(false, true)) .After(showControls); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); SystemSuspend(false); @@ -843,10 +847,10 @@ MediaSessionStateChanged(false, true)) .After(pauseControls); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); media_session_->Stop(MediaSession::SuspendType::UI); @@ -866,16 +870,16 @@ MediaSessionStateChanged(false, false)) .After(pauseControls); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); SystemSuspend(true); // This should reset the session and change it to a transient, so // hide the controls. - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); EXPECT_FALSE(IsControllable()); @@ -893,15 +897,15 @@ MediaSessionStateChanged(true, false)) .After(pauseControls); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); SystemSuspend(true); // This should reset the session and update the controls. - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); EXPECT_TRUE(IsControllable()); @@ -919,15 +923,15 @@ MediaSessionStateChanged(true, false)) .After(pauseControls); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); SystemSuspend(true); // This should resume the session and update the controls. - AddPlayer(media_session_observer.get(), 0, + AddPlayer(player_observer.get(), 0, media::MediaContentType::Persistent); EXPECT_TRUE(IsControllable()); @@ -942,10 +946,10 @@ MediaSessionStateChanged(true, true)) .After(showControls); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); UISuspend(); @@ -964,10 +968,10 @@ MediaSessionStateChanged(true, false)) .After(pauseControls); - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); UISuspend(); UIResume(); @@ -978,9 +982,9 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, DontResumeBySystemUISuspendedSessions) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); - StartNewPlayer(media_session_observer.get(), + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); UISuspend(); @@ -994,9 +998,9 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, AllowUIResumeForSystemSuspend) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); - StartNewPlayer(media_session_observer.get(), + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); SystemSuspend(true); @@ -1009,9 +1013,9 @@ } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ResumeSuspendFromUI) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); - StartNewPlayer(media_session_observer.get(), + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); UISuspend(); @@ -1024,9 +1028,9 @@ } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ResumeSuspendFromSystem) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); - StartNewPlayer(media_session_observer.get(), + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); SystemSuspend(true); @@ -1039,11 +1043,11 @@ } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, UMA_Suspended_SystemTransient) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); base::HistogramTester tester; - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); SystemSuspend(true); @@ -1058,11 +1062,11 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, UMA_Suspended_SystemPermantent) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); base::HistogramTester tester; - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); SystemSuspend(false); @@ -1075,11 +1079,11 @@ } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, UMA_Suspended_UI) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); base::HistogramTester tester; - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); UISuspend(); @@ -1092,11 +1096,11 @@ } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, UMA_Suspended_Multiple) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); base::HistogramTester tester; - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); UISuspend(); @@ -1119,11 +1123,11 @@ } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, UMA_Suspended_Crossing) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); base::HistogramTester tester; - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); UISuspend(); @@ -1145,11 +1149,11 @@ } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, UMA_Suspended_Stop) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); base::HistogramTester tester; - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); media_session_->Stop(MediaSession::SuspendType::UI); @@ -1175,8 +1179,8 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, UMA_ActiveTime_SimpleActivation) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); base::HistogramTester tester; MediaSessionUmaHelper* media_session_uma_helper = GetMediaSessionUMAHelper(); @@ -1185,7 +1189,7 @@ media_session_uma_helper->SetClockForTest( std::unique_ptr<base::SimpleTestTickClock>(clock)); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); clock->Advance(base::TimeDelta::FromMilliseconds(1000)); @@ -1199,8 +1203,8 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, UMA_ActiveTime_ActivationWithUISuspension) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); base::HistogramTester tester; MediaSessionUmaHelper* media_session_uma_helper = GetMediaSessionUMAHelper(); @@ -1209,7 +1213,7 @@ media_session_uma_helper->SetClockForTest( std::unique_ptr<base::SimpleTestTickClock>(clock)); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); clock->Advance(base::TimeDelta::FromMilliseconds(1000)); @@ -1229,8 +1233,8 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, UMA_ActiveTime_ActivationWithSystemSuspension) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); base::HistogramTester tester; MediaSessionUmaHelper* media_session_uma_helper = GetMediaSessionUMAHelper(); @@ -1239,7 +1243,7 @@ media_session_uma_helper->SetClockForTest( std::unique_ptr<base::SimpleTestTickClock>(clock)); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); clock->Advance(base::TimeDelta::FromMilliseconds(1000)); @@ -1259,8 +1263,8 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, UMA_ActiveTime_ActivateSuspendedButNotStopped) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); base::HistogramTester tester; MediaSessionUmaHelper* media_session_uma_helper = GetMediaSessionUMAHelper(); @@ -1269,7 +1273,7 @@ media_session_uma_helper->SetClockForTest( std::unique_ptr<base::SimpleTestTickClock>(clock)); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); clock->Advance(base::TimeDelta::FromMilliseconds(500)); SystemSuspend(true); @@ -1293,8 +1297,8 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, UMA_ActiveTime_ActivateSuspendStopTwice) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); base::HistogramTester tester; MediaSessionUmaHelper* media_session_uma_helper = GetMediaSessionUMAHelper(); @@ -1303,13 +1307,13 @@ media_session_uma_helper->SetClockForTest( std::unique_ptr<base::SimpleTestTickClock>(clock)); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); clock->Advance(base::TimeDelta::FromMilliseconds(500)); SystemSuspend(true); media_session_->Stop(MediaSession::SuspendType::UI); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); clock->Advance(base::TimeDelta::FromMilliseconds(5000)); SystemResume(); @@ -1324,8 +1328,8 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, UMA_ActiveTime_MultipleActivations) { - std::unique_ptr<MockMediaSessionObserver> media_session_observer( - new MockMediaSessionObserver); + std::unique_ptr<MockMediaSessionPlayerObserver> player_observer( + new MockMediaSessionPlayerObserver); base::HistogramTester tester; MediaSessionUmaHelper* media_session_uma_helper = GetMediaSessionUMAHelper(); @@ -1334,12 +1338,12 @@ media_session_uma_helper->SetClockForTest( std::unique_ptr<base::SimpleTestTickClock>(clock)); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); clock->Advance(base::TimeDelta::FromMilliseconds(10000)); - RemovePlayer(media_session_observer.get(), 0); + RemovePlayer(player_observer.get(), 0); - StartNewPlayer(media_session_observer.get(), + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); clock->Advance(base::TimeDelta::FromMilliseconds(1000)); media_session_->Stop(MediaSession::SuspendType::UI);
diff --git a/content/browser/media/session/media_session_controller.h b/content/browser/media/session/media_session_controller.h index ce5af65..4017f53 100644 --- a/content/browser/media/session/media_session_controller.h +++ b/content/browser/media/session/media_session_controller.h
@@ -7,7 +7,7 @@ #include "base/compiler_specific.h" #include "base/time/time.h" -#include "content/browser/media/session/media_session_observer.h" +#include "content/browser/media/session/media_session_player_observer.h" #include "content/common/content_export.h" #include "content/public/browser/web_contents_observer.h" @@ -23,8 +23,8 @@ // Helper class for controlling a single player's MediaSession instance. Sends // browser side MediaSession commands back to a player hosted in the renderer // process. -class CONTENT_EXPORT MediaSessionController : - NON_EXPORTED_BASE(public MediaSessionObserver) { +class CONTENT_EXPORT MediaSessionController + : NON_EXPORTED_BASE(public MediaSessionPlayerObserver) { public: MediaSessionController(const WebContentsObserver::MediaPlayerId& id, MediaWebContentsObserver* media_web_contents_observer);
diff --git a/content/browser/media/session/media_session_controllers_manager.cc b/content/browser/media/session/media_session_controllers_manager.cc index f0cf9d4..f924c9f 100644 --- a/content/browser/media/session/media_session_controllers_manager.cc +++ b/content/browser/media/session/media_session_controllers_manager.cc
@@ -7,7 +7,6 @@ #include "base/command_line.h" #include "content/browser/media/session/media_session.h" #include "content/browser/media/session/media_session_controller.h" -#include "content/browser/media/session/media_session_observer.h" #include "media/base/media_switches.h" namespace content {
diff --git a/content/browser/media/session/media_session_delegate.h b/content/browser/media/session/media_session_delegate.h deleted file mode 100644 index a0ed28b9..0000000 --- a/content/browser/media/session/media_session_delegate.h +++ /dev/null
@@ -1,30 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_DELEGATE_H_ -#define CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_DELEGATE_H_ - -#include "content/browser/media/session/audio_focus_manager.h" -#include "content/browser/media/session/media_session.h" - -namespace content { - -// MediaSessionDelegate is an interface abstracting audio focus handling for the -// MediaSession class. -class MediaSessionDelegate { - public: - // Factory method returning an implementation of MediaSessionDelegate. - static std::unique_ptr<MediaSessionDelegate> Create( - MediaSession* media_session); - - virtual ~MediaSessionDelegate() = default; - - virtual bool RequestAudioFocus( - AudioFocusManager::AudioFocusType audio_focus_type) = 0; - virtual void AbandonAudioFocus() = 0; -}; - -} // namespace content - -#endif // CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_DELEGATE_H_
diff --git a/content/browser/media/session/media_session_delegate_android.cc b/content/browser/media/session/media_session_delegate_android.cc deleted file mode 100644 index da826e9..0000000 --- a/content/browser/media/session/media_session_delegate_android.cc +++ /dev/null
@@ -1,101 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/media/session/media_session_delegate_android.h" - -#include "base/android/context_utils.h" -#include "base/android/jni_android.h" -#include "jni/MediaSessionDelegate_jni.h" - -using base::android::JavaParamRef; - -namespace content { - -// static -bool MediaSessionDelegateAndroid::Register(JNIEnv* env) { - return RegisterNativesImpl(env); -} - -MediaSessionDelegateAndroid::MediaSessionDelegateAndroid( - MediaSession* media_session) - : media_session_(media_session) { -} - -MediaSessionDelegateAndroid::~MediaSessionDelegateAndroid() { - JNIEnv* env = base::android::AttachCurrentThread(); - DCHECK(env); - Java_MediaSessionDelegate_tearDown(env, j_media_session_delegate_); -} - -void MediaSessionDelegateAndroid::Initialize() { - JNIEnv* env = base::android::AttachCurrentThread(); - DCHECK(env); - j_media_session_delegate_.Reset(Java_MediaSessionDelegate_create( - env, - base::android::GetApplicationContext(), - reinterpret_cast<intptr_t>(this))); -} - -bool MediaSessionDelegateAndroid::RequestAudioFocus( - AudioFocusManager::AudioFocusType audio_focus_type) { - JNIEnv* env = base::android::AttachCurrentThread(); - DCHECK(env); - return Java_MediaSessionDelegate_requestAudioFocus( - env, j_media_session_delegate_, - audio_focus_type == - AudioFocusManager::AudioFocusType::GainTransientMayDuck); -} - -void MediaSessionDelegateAndroid::AbandonAudioFocus() { - JNIEnv* env = base::android::AttachCurrentThread(); - DCHECK(env); - Java_MediaSessionDelegate_abandonAudioFocus(env, j_media_session_delegate_); -} - -void MediaSessionDelegateAndroid::OnSuspend( - JNIEnv*, const JavaParamRef<jobject>&, jboolean temporary) { - // TODO(mlamouri): this check makes it so that if a MediaSession is paused and - // then loses audio focus, it will still stay in the Suspended state. - // See https://crbug.com/539998 - if (!media_session_->IsActive()) - return; - - if (temporary) { - media_session_->Suspend(MediaSession::SuspendType::SYSTEM); - } else { - media_session_->Stop(MediaSession::SuspendType::SYSTEM); - } -} - -void MediaSessionDelegateAndroid::OnResume( - JNIEnv*, const JavaParamRef<jobject>&) { - if (!media_session_->IsReallySuspended()) - return; - - media_session_->Resume(MediaSession::SuspendType::SYSTEM); -} - -void MediaSessionDelegateAndroid::OnStartDucking(JNIEnv*, jobject) { - media_session_->StartDucking(); -} - -void MediaSessionDelegateAndroid::OnStopDucking(JNIEnv*, jobject) { - media_session_->StopDucking(); -} - -void MediaSessionDelegateAndroid::RecordSessionDuck( - JNIEnv*, const JavaParamRef<jobject>&) { - media_session_->RecordSessionDuck(); -} - -// static -std::unique_ptr<MediaSessionDelegate> MediaSessionDelegate::Create( - MediaSession* media_session) { - MediaSessionDelegateAndroid* delegate = - new MediaSessionDelegateAndroid(media_session); - delegate->Initialize(); - return std::unique_ptr<MediaSessionDelegate>(delegate); -} - -} // namespace content
diff --git a/content/browser/media/session/media_session_delegate_default.cc b/content/browser/media/session/media_session_delegate_default.cc deleted file mode 100644 index a921b07..0000000 --- a/content/browser/media/session/media_session_delegate_default.cc +++ /dev/null
@@ -1,66 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/media/session/media_session_delegate.h" - -#include "base/command_line.h" -#include "content/browser/media/session/audio_focus_manager.h" -#include "media/base/media_switches.h" - -namespace content { - -using AudioFocusType = AudioFocusManager::AudioFocusType; - -namespace { - -// MediaSessionDelegateDefault is the default implementation of -// MediaSessionDelegate which only handles audio focus between WebContents. -class MediaSessionDelegateDefault : public MediaSessionDelegate { - public: - explicit MediaSessionDelegateDefault(MediaSession* media_session); - ~MediaSessionDelegateDefault() override; - - // MediaSessionDelegate implementation. - bool RequestAudioFocus( - AudioFocusManager::AudioFocusType audio_focus_type) override; - void AbandonAudioFocus() override; - - private: - // Weak pointer because |this| is owned by |media_session_|. - MediaSession* media_session_; -}; - -} // anonymous namespace - -MediaSessionDelegateDefault::MediaSessionDelegateDefault( - MediaSession* media_session) - : media_session_(media_session) { -} - -MediaSessionDelegateDefault::~MediaSessionDelegateDefault() = default; - -bool MediaSessionDelegateDefault::RequestAudioFocus( - AudioFocusManager::AudioFocusType audio_focus_type) { - if (!base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableDefaultMediaSession)) { - return true; - } - - AudioFocusManager::GetInstance()->RequestAudioFocus(media_session_, - audio_focus_type); - return true; -} - -void MediaSessionDelegateDefault::AbandonAudioFocus() { - AudioFocusManager::GetInstance()->AbandonAudioFocus(media_session_); -} - -// static -std::unique_ptr<MediaSessionDelegate> MediaSessionDelegate::Create( - MediaSession* media_session) { - return std::unique_ptr<MediaSessionDelegate>( - new MediaSessionDelegateDefault(media_session)); -} - -} // namespace content
diff --git a/content/browser/media/session/media_session_observer.h b/content/browser/media/session/media_session_player_observer.h similarity index 65% rename from content/browser/media/session/media_session_observer.h rename to content/browser/media/session/media_session_player_observer.h index 4d9f4d49..95ba261 100644 --- a/content/browser/media/session/media_session_observer.h +++ b/content/browser/media/session/media_session_player_observer.h
@@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_OBSERVER_H_ -#define CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_OBSERVER_H_ +#ifndef CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_PLAYER_OBSERVER_H_ +#define CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_PLAYER_OBSERVER_H_ namespace content { -class MediaSessionObserver { +class MediaSessionPlayerObserver { public: - MediaSessionObserver() = default; - virtual ~MediaSessionObserver() = default; + MediaSessionPlayerObserver() = default; + virtual ~MediaSessionPlayerObserver() = default; // The given |player_id| has been suspended by the MediaSession. virtual void OnSuspend(int player_id) = 0; @@ -26,4 +26,4 @@ } // namespace content -#endif // CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_OBSERVER_H_ +#endif // CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_PLAYER_OBSERVER_H_
diff --git a/content/browser/media/session/mock_media_session_observer.cc b/content/browser/media/session/mock_media_session_observer.cc deleted file mode 100644 index edc213a..0000000 --- a/content/browser/media/session/mock_media_session_observer.cc +++ /dev/null
@@ -1,70 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/media/session/mock_media_session_observer.h" - -#include "testing/gtest/include/gtest/gtest.h" - -namespace content { - -MockMediaSessionObserver::MockMediaSessionObserver() = default; - -MockMediaSessionObserver::~MockMediaSessionObserver() = default; - -void MockMediaSessionObserver::OnSuspend(int player_id) { - EXPECT_GE(player_id, 0); - EXPECT_GT(players_.size(), static_cast<size_t>(player_id)); - - ++received_suspend_calls_; - players_[player_id].is_playing_ = false; -} - -void MockMediaSessionObserver::OnResume(int player_id) { - EXPECT_GE(player_id, 0); - EXPECT_GT(players_.size(), static_cast<size_t>(player_id)); - - ++received_resume_calls_; - players_[player_id].is_playing_ = true; -} - -void MockMediaSessionObserver::OnSetVolumeMultiplier( - int player_id, double volume_multiplier) { - EXPECT_GE(player_id, 0); - EXPECT_GT(players_.size(), static_cast<size_t>(player_id)); - - EXPECT_GE(volume_multiplier, 0.0f); - EXPECT_LE(volume_multiplier, 1.0f); - - players_[player_id].volume_multiplier_ = volume_multiplier; -} - -int MockMediaSessionObserver::StartNewPlayer() { - players_.push_back(MockPlayer(true, 1.0f)); - return players_.size() - 1; -} - -bool MockMediaSessionObserver::IsPlaying(size_t player_id) { - EXPECT_GT(players_.size(), player_id); - return players_[player_id].is_playing_; -} - -double MockMediaSessionObserver::GetVolumeMultiplier(size_t player_id) { - EXPECT_GT(players_.size(), player_id); - return players_[player_id].volume_multiplier_; -} - -void MockMediaSessionObserver::SetPlaying(size_t player_id, bool playing) { - EXPECT_GT(players_.size(), player_id); - players_[player_id].is_playing_ = playing; -} - -int MockMediaSessionObserver::received_suspend_calls() const { - return received_suspend_calls_; -} - -int MockMediaSessionObserver::received_resume_calls() const { - return received_resume_calls_; -} - -} // namespace content
diff --git a/content/browser/media/session/mock_media_session_player_observer.cc b/content/browser/media/session/mock_media_session_player_observer.cc new file mode 100644 index 0000000..b26bc40 --- /dev/null +++ b/content/browser/media/session/mock_media_session_player_observer.cc
@@ -0,0 +1,72 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/media/session/mock_media_session_player_observer.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { + +MockMediaSessionPlayerObserver::MockMediaSessionPlayerObserver() = default; + +MockMediaSessionPlayerObserver::~MockMediaSessionPlayerObserver() = default; + +void MockMediaSessionPlayerObserver::OnSuspend(int player_id) { + EXPECT_GE(player_id, 0); + EXPECT_GT(players_.size(), static_cast<size_t>(player_id)); + + ++received_suspend_calls_; + players_[player_id].is_playing_ = false; +} + +void MockMediaSessionPlayerObserver::OnResume(int player_id) { + EXPECT_GE(player_id, 0); + EXPECT_GT(players_.size(), static_cast<size_t>(player_id)); + + ++received_resume_calls_; + players_[player_id].is_playing_ = true; +} + +void MockMediaSessionPlayerObserver::OnSetVolumeMultiplier( + int player_id, + double volume_multiplier) { + EXPECT_GE(player_id, 0); + EXPECT_GT(players_.size(), static_cast<size_t>(player_id)); + + EXPECT_GE(volume_multiplier, 0.0f); + EXPECT_LE(volume_multiplier, 1.0f); + + players_[player_id].volume_multiplier_ = volume_multiplier; +} + +int MockMediaSessionPlayerObserver::StartNewPlayer() { + players_.push_back(MockPlayer(true, 1.0f)); + return players_.size() - 1; +} + +bool MockMediaSessionPlayerObserver::IsPlaying(size_t player_id) { + EXPECT_GT(players_.size(), player_id); + return players_[player_id].is_playing_; +} + +double MockMediaSessionPlayerObserver::GetVolumeMultiplier(size_t player_id) { + EXPECT_GT(players_.size(), player_id); + return players_[player_id].volume_multiplier_; +} + +void MockMediaSessionPlayerObserver::SetPlaying(size_t player_id, + bool playing) { + EXPECT_GT(players_.size(), player_id); + players_[player_id].is_playing_ = playing; +} + +int MockMediaSessionPlayerObserver::received_suspend_calls() const { + return received_suspend_calls_; +} + +int MockMediaSessionPlayerObserver::received_resume_calls() const { + return received_resume_calls_; +} + +} // namespace content
diff --git a/content/browser/media/session/mock_media_session_observer.h b/content/browser/media/session/mock_media_session_player_observer.h similarity index 74% rename from content/browser/media/session/mock_media_session_observer.h rename to content/browser/media/session/mock_media_session_player_observer.h index 03913bc..824db68 100644 --- a/content/browser/media/session/mock_media_session_observer.h +++ b/content/browser/media/session/mock_media_session_player_observer.h
@@ -5,18 +5,18 @@ #include <stddef.h> #include <vector> -#include "content/browser/media/session/media_session_observer.h" +#include "content/browser/media/session/media_session_player_observer.h" namespace content { -// MockMediaSessionObserver is a mock implementation of MediaSessionObserver to -// be used in tests. -class MockMediaSessionObserver : public MediaSessionObserver { +// MockMediaSessionPlayerObserver is a mock implementation of +// MediaSessionPlayerObserver to be used in tests. +class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver { public: - MockMediaSessionObserver(); - ~MockMediaSessionObserver() override; + MockMediaSessionPlayerObserver(); + ~MockMediaSessionPlayerObserver() override; - // Implements MediaSessionObserver. + // Implements MediaSessionPlayerObserver. void OnSuspend(int player_id) override; void OnResume(int player_id) override; void OnSetVolumeMultiplier(int player_id, double volume_multiplier) override; @@ -42,8 +42,7 @@ struct MockPlayer { public: MockPlayer(bool is_playing = true, double volume_multiplier = 1.0f) - : is_playing_(is_playing), - volume_multiplier_(volume_multiplier) {} + : is_playing_(is_playing), volume_multiplier_(volume_multiplier) {} bool is_playing_; double volume_multiplier_; };
diff --git a/content/browser/media/session/pepper_player_delegate.h b/content/browser/media/session/pepper_player_delegate.h index bf3a39bd..db8b846 100644 --- a/content/browser/media/session/pepper_player_delegate.h +++ b/content/browser/media/session/pepper_player_delegate.h
@@ -6,14 +6,14 @@ #define CONTENT_BROWSER_MEDIA_SESSION_PEPPER_PLAYER_DELEGATE_H_ #include "base/macros.h" -#include "content/browser/media/session/media_session_observer.h" +#include "content/browser/media/session/media_session_player_observer.h" #include "content/browser/web_contents/web_contents_impl.h" namespace content { class PepperWebContentsObserver; -class PepperPlayerDelegate : public MediaSessionObserver { +class PepperPlayerDelegate : public MediaSessionPlayerObserver { public: // The Id can only be 0 for PepperPlayerDelegate. Declare the constant here so // it can be used elsewhere. @@ -23,7 +23,7 @@ WebContentsImpl* contents, int32_t pp_instance); ~PepperPlayerDelegate() override; - // MediaSessionObserver implementation. + // MediaSessionPlayerObserver implementation. void OnSuspend(int player_id) override; void OnResume(int player_id) override; void OnSetVolumeMultiplier(int player_id,
diff --git a/content/browser/notifications/notification_message_filter.cc b/content/browser/notifications/notification_message_filter.cc index 42fd875..6efe1d2 100644 --- a/content/browser/notifications/notification_message_filter.cc +++ b/content/browser/notifications/notification_message_filter.cc
@@ -117,7 +117,10 @@ OnShowPersistentNotification) IPC_MESSAGE_HANDLER(PlatformNotificationHostMsg_GetNotifications, OnGetNotifications) - IPC_MESSAGE_HANDLER(PlatformNotificationHostMsg_Close, OnCloseNotification) + IPC_MESSAGE_HANDLER(PlatformNotificationHostMsg_Close, + OnClosePlatformNotification) + IPC_MESSAGE_HANDLER(PlatformNotificationHostMsg_ClosePersistent, + OnClosePersistentNotification) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() @@ -127,7 +130,8 @@ void NotificationMessageFilter::OverrideThreadForMessage( const IPC::Message& message, content::BrowserThread::ID* thread) { - if (message.type() == PlatformNotificationHostMsg_Show::ID) + if (message.type() == PlatformNotificationHostMsg_Show::ID || + message.type() == PlatformNotificationHostMsg_Close::ID) *thread = BrowserThread::UI; } @@ -302,7 +306,26 @@ request_id, persistent_notifications)); } -void NotificationMessageFilter::OnCloseNotification( +void NotificationMessageFilter::OnClosePlatformNotification( + const GURL& origin, + const std::string& tag, + int non_persistent_notification_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (!RenderProcessHost::FromID(process_id_)) + return; + + std::string notification_id = + GetNotificationIdGenerator()->GenerateForNonPersistentNotification( + origin, tag, non_persistent_notification_id, process_id_); + + if (!close_closures_.count(notification_id)) + return; + + close_closures_[notification_id].Run(); + close_closures_.erase(notification_id); +} + +void NotificationMessageFilter::OnClosePersistentNotification( const GURL& origin, const std::string& tag, const std::string& notification_id) { @@ -317,38 +340,19 @@ GetContentClient()->browser()->GetPlatformNotificationService(); DCHECK(service); - if (NotificationIdGenerator::IsPersistentNotification(notification_id)) { - // There's no point in waiting until the database data has been removed - // before closing the notification presented to the user. - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&PlatformNotificationService::ClosePersistentNotification, - base::Unretained(service), // The service is a singleton. - browser_context_, notification_id)); + // There's no point in waiting until the database data has been removed before + // closing the notification presented to the user. Post that task immediately. + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&PlatformNotificationService::ClosePersistentNotification, + base::Unretained(service), // The service is a singleton. + browser_context_, notification_id)); - notification_context_->DeleteNotificationData( - notification_id, origin, - base::Bind( - &NotificationMessageFilter::DidDeletePersistentNotificationData, - weak_factory_io_.GetWeakPtr())); - - return; - } - - if (NotificationIdGenerator::IsNonPersistentNotification(notification_id)) { - if (!close_closures_.count(notification_id)) - return; - - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - close_closures_[notification_id]); - - close_closures_.erase(notification_id); - return; - } - - // The renderer may have been compromised if the given |notification_id| - // doesn't map to either a persistent or a non-persistent notification. - bad_message::ReceivedBadMessage(this, bad_message::NMF_INVALID_ID_CLOSE); + notification_context_->DeleteNotificationData( + notification_id, origin, + base::Bind( + &NotificationMessageFilter::DidDeletePersistentNotificationData, + weak_factory_io_.GetWeakPtr())); } void NotificationMessageFilter::DidDeletePersistentNotificationData(
diff --git a/content/browser/notifications/notification_message_filter.h b/content/browser/notifications/notification_message_filter.h index eaba08a..25d69c6 100644 --- a/content/browser/notifications/notification_message_filter.h +++ b/content/browser/notifications/notification_message_filter.h
@@ -73,9 +73,12 @@ int64_t service_worker_registration_id, const GURL& origin, const std::string& filter_tag); - void OnCloseNotification(const GURL& origin, - const std::string& tag, - const std::string& notification_id); + void OnClosePlatformNotification(const GURL& origin, + const std::string& tag, + int non_persistent_notification_id); + void OnClosePersistentNotification(const GURL& origin, + const std::string& tag, + const std::string& notification_id); // Callback to be invoked by the notification context when the notification // data for the persistent notification may have been written, as indicated by
diff --git a/content/browser/notifications/page_notification_delegate.cc b/content/browser/notifications/page_notification_delegate.cc index 6b3b695..4785829 100644 --- a/content/browser/notifications/page_notification_delegate.cc +++ b/content/browser/notifications/page_notification_delegate.cc
@@ -26,8 +26,8 @@ if (!sender) return; - sender->Send(new PlatformNotificationMsg_DidShow( - non_persistent_notification_id_, notification_id_)); + sender->Send( + new PlatformNotificationMsg_DidShow(non_persistent_notification_id_)); } void PageNotificationDelegate::NotificationClosed() { @@ -35,8 +35,8 @@ if (!sender) return; - sender->Send(new PlatformNotificationMsg_DidClose( - non_persistent_notification_id_, notification_id_)); + sender->Send( + new PlatformNotificationMsg_DidClose(non_persistent_notification_id_)); static_cast<RenderProcessHostImpl*>(sender) ->notification_message_filter()
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc index ee6c760..39eb7da 100644 --- a/content/browser/renderer_host/render_view_host_impl.cc +++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -464,7 +464,6 @@ prefs.inert_visual_viewport = command_line.HasSwitch(switches::kInertVisualViewport); - prefs.pinch_overlay_scrollbar_thickness = 10; prefs.use_solid_color_scrollbars = ui::IsOverlayScrollbarEnabled(); prefs.history_entry_requires_user_gesture =
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm index ff4d50f..8a400e7 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -2373,14 +2373,34 @@ - (void)showLookUpDictionaryOverlayFromRange:(NSRange)range targetView:(NSView*)targetView { + RenderWidgetHostImpl* widgetHost = renderWidgetHostView_->render_widget_host_; + if (!widgetHost || !widgetHost->delegate()) + return; + widgetHost = widgetHost->delegate()->GetFocusedRenderWidgetHost(widgetHost); + + if (!widgetHost) + return; + + // TODO(ekaramad): The position reported by the renderer is with respect to + // |widgetHost|'s coordinate space with y-axis inverted to conform to AppKit + // coordinate system. The point will need to be transformed into root view's + // coordinate system (RenderWidgetHostViewMac in this case). However, since + // the callback is invoked on IO thread it will require some thread hopping to + // do so. For this reason, for now, we accept this non-ideal way of fixing the + // point offset manually from the view bounds. This should be revisited when + // fixing issues in TextInputClientMac (https://crbug.com/643233). + gfx::Rect root_box = renderWidgetHostView_->GetViewBounds(); + gfx::Rect view_box = widgetHost->GetView()->GetViewBounds(); + TextInputClientMac::GetInstance()->GetStringFromRange( - renderWidgetHostView_->render_widget_host_, range, - ^(NSAttributedString* string, NSPoint baselinePoint) { + widgetHost, range, ^(NSAttributedString* string, NSPoint baselinePoint) { + baselinePoint.x += view_box.origin().x() - root_box.origin().x(); + baselinePoint.y += + root_box.bottom_left().y() - view_box.bottom_left().y(); [self showLookUpDictionaryOverlayInternal:string baselinePoint:baselinePoint targetView:targetView]; - } - ); + }); } - (void)showLookUpDictionaryOverlayAtPoint:(NSPoint)point {
diff --git a/content/child/notifications/notification_manager.cc b/content/child/notifications/notification_manager.cc index 042cbf5b..45af387 100644 --- a/content/child/notifications/notification_manager.cc +++ b/content/child/notifications/notification_manager.cc
@@ -43,6 +43,14 @@ static base::LazyInstance<base::ThreadLocalPointer<NotificationManager>>::Leaky g_notification_manager_tls = LAZY_INSTANCE_INITIALIZER; +NotificationManager::ActiveNotificationData::ActiveNotificationData( + blink::WebNotificationDelegate* delegate, + const GURL& origin, + const std::string& tag) + : delegate(delegate), origin(origin), tag(tag) {} + +NotificationManager::ActiveNotificationData::~ActiveNotificationData() {} + NotificationManager::NotificationManager( ThreadSafeSender* thread_safe_sender, NotificationDispatcher* notification_dispatcher) @@ -82,16 +90,18 @@ GURL origin_gurl = blink::WebStringToGURL(origin.toString()); - int non_persistent_notification_id = + int notification_id = notification_dispatcher_->GenerateNotificationId(CurrentWorkerId()); - non_persistent_notifications_[non_persistent_notification_id] = delegate; + active_page_notifications_[notification_id] = ActiveNotificationData( + delegate, origin_gurl, + base::UTF16ToUTF8(base::StringPiece16(notification_data.tag))); // TODO(mkwst): This is potentially doing the wrong thing with unique // origins. Perhaps also 'file:', 'blob:' and 'filesystem:'. See // https://crbug.com/490074 for detail. thread_safe_sender_->Send(new PlatformNotificationHostMsg_Show( - non_persistent_notification_id, origin_gurl, + notification_id, origin_gurl, ToPlatformNotificationData(notification_data), ToNotificationResources(std::move(notification_resources)))); } @@ -176,61 +186,54 @@ base::UTF16ToUTF8(base::StringPiece16(filter_tag)))); } -void NotificationManager::close(const blink::WebSecurityOrigin& origin, - const blink::WebString& tag, - const blink::WebString& notification_id) { - const std::string notification_id_str = - base::UTF16ToUTF8(base::StringPiece16(notification_id)); +void NotificationManager::close(blink::WebNotificationDelegate* delegate) { + for (auto& iter : active_page_notifications_) { + if (iter.second.delegate != delegate) + continue; - // Remove the stored local state for non-persistent notifications. - auto iter = non_persistent_notification_ids_.find(notification_id_str); - if (iter != non_persistent_notification_ids_.end()) { - int non_persistent_notification_id = iter->second; - - non_persistent_notifications_.erase(non_persistent_notification_id); - non_persistent_notification_ids_.erase(iter); + thread_safe_sender_->Send(new PlatformNotificationHostMsg_Close( + iter.second.origin, iter.second.tag, iter.first)); + active_page_notifications_.erase(iter.first); + return; } - thread_safe_sender_->Send(new PlatformNotificationHostMsg_Close( + // It should not be possible for Blink to call close() on a Notification which + // does not exist in either the pending or active notification lists. + NOTREACHED(); +} + +void NotificationManager::closePersistent( + const blink::WebSecurityOrigin& origin, + const blink::WebString& tag, + const blink::WebString& notification_id) { + thread_safe_sender_->Send(new PlatformNotificationHostMsg_ClosePersistent( // TODO(mkwst): This is potentially doing the wrong thing with unique // origins. Perhaps also 'file:', 'blob:' and 'filesystem:'. See // https://crbug.com/490074 for detail. blink::WebStringToGURL(origin.toString()), - base::UTF16ToUTF8(base::StringPiece16(tag)), notification_id_str)); + base::UTF16ToUTF8(base::StringPiece16(tag)), + base::UTF16ToUTF8(base::StringPiece16(notification_id)))); } void NotificationManager::notifyDelegateDestroyed( blink::WebNotificationDelegate* delegate) { - for (auto iter = non_persistent_notifications_.begin(); - iter != non_persistent_notifications_.end(); iter++) { - if (iter->second != delegate) + for (auto& iter : active_page_notifications_) { + if (iter.second.delegate != delegate) continue; - int non_persistent_notification_id = iter->first; - - // Remove the notification's ID association from the local state as well. - for (auto assoc_iter = non_persistent_notification_ids_.begin(); - assoc_iter != non_persistent_notification_ids_.end(); assoc_iter++) { - if (assoc_iter->second != non_persistent_notification_id) - continue; - - non_persistent_notification_ids_.erase(assoc_iter); - break; - } - - non_persistent_notifications_.erase(iter); - break; + active_page_notifications_.erase(iter.first); + return; } } bool NotificationManager::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(NotificationManager, message) - IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidShow, OnDidShow) + IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidShow, OnDidShow); IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidShowPersistent, OnDidShowPersistent) - IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidClose, OnDidClose) - IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidClick, OnDidClick) + IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidClose, OnDidClose); + IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidClick, OnDidClick); IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidGetNotifications, OnDidGetNotifications) IPC_MESSAGE_UNHANDLED(handled = false) @@ -239,19 +242,12 @@ return handled; } -void NotificationManager::OnDidShow(int non_persistent_notification_id, - const std::string& notification_id) { - const auto iter = - non_persistent_notifications_.find(non_persistent_notification_id); +void NotificationManager::OnDidShow(int notification_id) { + const auto& iter = active_page_notifications_.find(notification_id); + if (iter == active_page_notifications_.end()) + return; - if (iter == non_persistent_notifications_.end()) - return; // The notification has been destroyed by Blink since. - - non_persistent_notification_ids_[notification_id] = - non_persistent_notification_id; - - blink::WebNotificationDelegate* delegate = iter->second; - delegate->didShowNotification(blink::WebString::fromUTF8(notification_id)); + iter->second.delegate->dispatchShowEvent(); } void NotificationManager::OnDidShowPersistent(int request_id, bool success) { @@ -270,26 +266,22 @@ pending_show_notification_requests_.Remove(request_id); } -void NotificationManager::OnDidClose(int non_persistent_notification_id, - const std::string& notification_id) { - const auto iter = - non_persistent_notifications_.find(non_persistent_notification_id); - if (iter == non_persistent_notifications_.end()) +void NotificationManager::OnDidClose(int notification_id) { + const auto& iter = active_page_notifications_.find(notification_id); + if (iter == active_page_notifications_.end()) return; - iter->second->didCloseNotification(); + iter->second.delegate->dispatchCloseEvent(); - non_persistent_notifications_.erase(iter); - non_persistent_notification_ids_.erase(notification_id); + active_page_notifications_.erase(iter); } -void NotificationManager::OnDidClick(int non_persistent_notification_id) { - const auto iter = - non_persistent_notifications_.find(non_persistent_notification_id); - if (iter == non_persistent_notifications_.end()) +void NotificationManager::OnDidClick(int notification_id) { + const auto& iter = active_page_notifications_.find(notification_id); + if (iter == active_page_notifications_.end()) return; - iter->second->didClickNotification(); + iter->second.delegate->dispatchClickEvent(); } void NotificationManager::OnDidGetNotifications(
diff --git a/content/child/notifications/notification_manager.h b/content/child/notifications/notification_manager.h index da673a459..b556c8e1 100644 --- a/content/child/notifications/notification_manager.h +++ b/content/child/notifications/notification_manager.h
@@ -56,9 +56,10 @@ const blink::WebString& filter_tag, blink::WebServiceWorkerRegistration* service_worker_registration, blink::WebNotificationGetCallbacks* callbacks) override; - void close(const blink::WebSecurityOrigin& origin, - const blink::WebString& tag, - const blink::WebString& notification_id) override; + void close(blink::WebNotificationDelegate* delegate) override; + void closePersistent(const blink::WebSecurityOrigin& origin, + const blink::WebString& tag, + const blink::WebString& notification_id) override; void notifyDelegateDestroyed( blink::WebNotificationDelegate* delegate) override; @@ -70,12 +71,10 @@ NotificationDispatcher* notification_dispatcher); // IPC message handlers. - void OnDidShow(int non_persistent_notification_id, - const std::string& notification_id); + void OnDidShow(int notification_id); void OnDidShowPersistent(int request_id, bool success); - void OnDidClose(int non_persistent_notification_id, - const std::string& notification_id); - void OnDidClick(int non_persistent_notification_id); + void OnDidClose(int notification_id); + void OnDidClick(int notification_id); void OnDidGetNotifications( int request_id, const std::vector<PersistentNotificationInfo>& notification_infos); @@ -91,14 +90,21 @@ IDMap<blink::WebNotificationShowCallbacks, IDMapOwnPointer> pending_show_notification_requests_; - // Map from the notification ID to the associated delegate for non-persistent - // notifications, for the purposes of triggering events. - std::unordered_map<int, blink::WebNotificationDelegate*> - non_persistent_notifications_; + // Structure holding the information for active non-persistent notifications. + struct ActiveNotificationData { + ActiveNotificationData() = default; + ActiveNotificationData(blink::WebNotificationDelegate* delegate, + const GURL& origin, + const std::string& tag); + ~ActiveNotificationData(); - // Association from the notification ID string to the non-persistent - // notification ID for non-persistent notifications. - std::unordered_map<std::string, int> non_persistent_notification_ids_; + blink::WebNotificationDelegate* delegate = nullptr; + GURL origin; + std::string tag; + }; + + // Map to store the delegate associated with a notification request Id. + std::unordered_map<int, ActiveNotificationData> active_page_notifications_; DISALLOW_COPY_AND_ASSIGN(NotificationManager); };
diff --git a/content/common/platform_notification_messages.h b/content/common/platform_notification_messages.h index 7646c0e..02bb9f3a 100644 --- a/content/common/platform_notification_messages.h +++ b/content/common/platform_notification_messages.h
@@ -71,18 +71,16 @@ // Messages sent from the browser to the renderer. // Informs the renderer that the browser has displayed the notification. -IPC_MESSAGE_CONTROL2(PlatformNotificationMsg_DidShow, - int /* non_persistent_notification_id */, - std::string /* notification_id */) +IPC_MESSAGE_CONTROL1(PlatformNotificationMsg_DidShow, + int /* notification_id */) // Informs the renderer that the notification has been closed. -IPC_MESSAGE_CONTROL2(PlatformNotificationMsg_DidClose, - int /* non_persistent_notification_id */, - std::string /* notification_id */) +IPC_MESSAGE_CONTROL1(PlatformNotificationMsg_DidClose, + int /* notification_id */) // Informs the renderer that the notification has been clicked on. IPC_MESSAGE_CONTROL1(PlatformNotificationMsg_DidClick, - int /* non_persistent_notification_id */) + int /* notification_id */) // Reply to PlatformNotificationHostMsg_ShowPersistent indicating that a // persistent notification has been shown on the platform (if |success| is @@ -124,4 +122,9 @@ IPC_MESSAGE_CONTROL3(PlatformNotificationHostMsg_Close, GURL /* origin */, std::string /* tag */, + int /* non_persistent_notification_id */) + +IPC_MESSAGE_CONTROL3(PlatformNotificationHostMsg_ClosePersistent, + GURL /* origin */, + std::string /* tag */, std::string /* notification_id */)
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn index 877df73..2633cb5 100644 --- a/content/public/android/BUILD.gn +++ b/content/public/android/BUILD.gn
@@ -100,6 +100,7 @@ "java/src/org/chromium/content/browser/AppWebMessagePort.java", "java/src/org/chromium/content/browser/AppWebMessagePortService.java", "java/src/org/chromium/content/browser/ActivityContentVideoViewEmbedder.java", + "java/src/org/chromium/content/browser/AudioFocusDelegate.java", "java/src/org/chromium/content/browser/BackgroundSyncNetworkObserver.java", "java/src/org/chromium/content/browser/BindingManager.java", "java/src/org/chromium/content/browser/BindingManagerImpl.java", @@ -124,7 +125,6 @@ "java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java", "java/src/org/chromium/content/browser/JavascriptInterface.java", "java/src/org/chromium/content/browser/MediaResourceGetter.java", - "java/src/org/chromium/content/browser/MediaSessionDelegate.java", "java/src/org/chromium/content/browser/MediaThrottler.java", "java/src/org/chromium/content/browser/MemoryMonitorAndroid.java", "java/src/org/chromium/content/browser/MotionEventSynthesizer.java", @@ -295,6 +295,7 @@ "java/src/org/chromium/content/app/ChildProcessServiceImpl.java", "java/src/org/chromium/content/app/ContentMain.java", "java/src/org/chromium/content/browser/AppWebMessagePortService.java", + "java/src/org/chromium/content/browser/AudioFocusDelegate.java", "java/src/org/chromium/content/browser/BackgroundSyncNetworkObserver.java", "java/src/org/chromium/content/browser/BrowserStartupController.java", "java/src/org/chromium/content/browser/ChildProcessLauncher.java", @@ -306,7 +307,6 @@ "java/src/org/chromium/content/browser/InterfaceRegistrarImpl.java", "java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java", "java/src/org/chromium/content/browser/MediaResourceGetter.java", - "java/src/org/chromium/content/browser/MediaSessionDelegate.java", "java/src/org/chromium/content/browser/MediaThrottler.java", "java/src/org/chromium/content/browser/MemoryMonitorAndroid.java", "java/src/org/chromium/content/browser/MotionEventSynthesizer.java",
diff --git a/content/public/android/java/src/org/chromium/content/browser/AudioFocusDelegate.java b/content/public/android/java/src/org/chromium/content/browser/AudioFocusDelegate.java new file mode 100644 index 0000000..b8df64a --- /dev/null +++ b/content/public/android/java/src/org/chromium/content/browser/AudioFocusDelegate.java
@@ -0,0 +1,115 @@ +// 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. + +package org.chromium.content.browser; + +import android.content.Context; +import android.media.AudioManager; + +import org.chromium.base.Log; +import org.chromium.base.ThreadUtils; +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; + +/** + * AudioFocusDelegate is the Java counterpart of content::AudioFocusDelegateAndroid. + * It is being used to communicate from content::AudioFocusDelegateAndroid + * (C++) to the Android system. A AudioFocusDelegate is implementingf + * OnAudioFocusChangeListener, making it an audio focus holder for Android. Thus + * two instances of AudioFocusDelegate can't have audio focus at the same + * time. A AudioFocusDelegate will use the type requested from its C++ + * counterpart and will resume its play using the same type if it were to + * happen, for example, when it got temporarily suspended by a transient sound + * like a notification. + */ +@JNINamespace("content") +public class AudioFocusDelegate implements AudioManager.OnAudioFocusChangeListener { + private static final String TAG = "MediaSession"; + + private Context mContext; + private int mFocusType; + private boolean mIsDucking = false; + + // Native pointer to C++ content::AudioFocusDelegateAndroid. + // It will be set to 0 when the native AudioFocusDelegateAndroid object is destroyed. + private long mNativeAudioFocusDelegateAndroid; + + private AudioFocusDelegate(final Context context, long nativeAudioFocusDelegateAndroid) { + mContext = context; + mNativeAudioFocusDelegateAndroid = nativeAudioFocusDelegateAndroid; + } + + @CalledByNative + private static AudioFocusDelegate create( + Context context, long nativeAudioFocusDelegateAndroid) { + return new AudioFocusDelegate(context, nativeAudioFocusDelegateAndroid); + } + + @CalledByNative + private void tearDown() { + assert ThreadUtils.runningOnUiThread(); + abandonAudioFocus(); + mNativeAudioFocusDelegateAndroid = 0; + } + + @CalledByNative + private boolean requestAudioFocus(boolean transientFocus) { + assert ThreadUtils.runningOnUiThread(); + mFocusType = transientFocus ? AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK + : AudioManager.AUDIOFOCUS_GAIN; + return requestAudioFocusInternal(); + } + + @CalledByNative + private void abandonAudioFocus() { + assert ThreadUtils.runningOnUiThread(); + AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + am.abandonAudioFocus(this); + } + + private boolean requestAudioFocusInternal() { + AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + + int result = am.requestAudioFocus(this, AudioManager.STREAM_MUSIC, mFocusType); + return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED; + } + + @Override + public void onAudioFocusChange(int focusChange) { + assert ThreadUtils.runningOnUiThread(); + if (mNativeAudioFocusDelegateAndroid == 0) return; + + switch (focusChange) { + case AudioManager.AUDIOFOCUS_GAIN: + if (mIsDucking) { + nativeOnStopDucking(mNativeAudioFocusDelegateAndroid); + mIsDucking = false; + } else { + nativeOnResume(mNativeAudioFocusDelegateAndroid); + } + break; + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: + nativeOnSuspend(mNativeAudioFocusDelegateAndroid, true); + break; + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: + mIsDucking = true; + nativeRecordSessionDuck(mNativeAudioFocusDelegateAndroid); + nativeOnStartDucking(mNativeAudioFocusDelegateAndroid); + break; + case AudioManager.AUDIOFOCUS_LOSS: + abandonAudioFocus(); + nativeOnSuspend(mNativeAudioFocusDelegateAndroid, false); + break; + default: + Log.w(TAG, "onAudioFocusChange called with unexpected value %d", focusChange); + break; + } + } + + private native void nativeOnSuspend(long nativeAudioFocusDelegateAndroid, boolean temporary); + private native void nativeOnResume(long nativeAudioFocusDelegateAndroid); + private native void nativeOnStartDucking(long nativeAudioFocusDelegateAndroid); + private native void nativeOnStopDucking(long nativeAudioFocusDelegateAndroid); + private native void nativeRecordSessionDuck(long nativeAudioFocusDelegateAndroid); +}
diff --git a/content/public/android/java/src/org/chromium/content/browser/MediaSessionDelegate.java b/content/public/android/java/src/org/chromium/content/browser/MediaSessionDelegate.java deleted file mode 100644 index 36f1c014..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/MediaSessionDelegate.java +++ /dev/null
@@ -1,115 +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. - -package org.chromium.content.browser; - -import android.content.Context; -import android.media.AudioManager; - -import org.chromium.base.Log; -import org.chromium.base.ThreadUtils; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; - -/** - * MediaSession is the Java counterpart of content::MediaSessionDelegateAndroid. - * It is being used to communicate from content::MediaSessionDelegateAndroid - * (C++) to the Android system. A MediaSessionDelegate is implementingf - * OnAudioFocusChangeListener, making it an audio focus holder for Android. Thus - * two instances of MediaSessionDelegate can't have audio focus at the same - * time. A MediaSessionDelegate will use the type requested from its C++ - * counterpart and will resume its play using the same type if it were to - * happen, for example, when it got temporarily suspended by a transient sound - * like a notification. - */ -@JNINamespace("content") -public class MediaSessionDelegate implements AudioManager.OnAudioFocusChangeListener { - private static final String TAG = "MediaSession"; - - private Context mContext; - private int mFocusType; - private boolean mIsDucking = false; - - // Native pointer to C++ content::MediaSessionDelegateAndroid. - // It will be set to 0 when the native MediaSessionDelegateAndroid object is destroyed. - private long mNativeMediaSessionDelegateAndroid; - - private MediaSessionDelegate(final Context context, long nativeMediaSessionDelegateAndroid) { - mContext = context; - mNativeMediaSessionDelegateAndroid = nativeMediaSessionDelegateAndroid; - } - - @CalledByNative - private static MediaSessionDelegate create(Context context, - long nativeMediaSessionDelegateAndroid) { - return new MediaSessionDelegate(context, nativeMediaSessionDelegateAndroid); - } - - @CalledByNative - private void tearDown() { - assert ThreadUtils.runningOnUiThread(); - abandonAudioFocus(); - mNativeMediaSessionDelegateAndroid = 0; - } - - @CalledByNative - private boolean requestAudioFocus(boolean transientFocus) { - assert ThreadUtils.runningOnUiThread(); - mFocusType = transientFocus ? AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK - : AudioManager.AUDIOFOCUS_GAIN; - return requestAudioFocusInternal(); - } - - @CalledByNative - private void abandonAudioFocus() { - assert ThreadUtils.runningOnUiThread(); - AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); - am.abandonAudioFocus(this); - } - - private boolean requestAudioFocusInternal() { - AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); - - int result = am.requestAudioFocus(this, AudioManager.STREAM_MUSIC, mFocusType); - return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED; - } - - @Override - public void onAudioFocusChange(int focusChange) { - assert ThreadUtils.runningOnUiThread(); - if (mNativeMediaSessionDelegateAndroid == 0) return; - - switch (focusChange) { - case AudioManager.AUDIOFOCUS_GAIN: - if (mIsDucking) { - nativeOnStopDucking(mNativeMediaSessionDelegateAndroid); - mIsDucking = false; - } else { - nativeOnResume(mNativeMediaSessionDelegateAndroid); - } - break; - case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: - nativeOnSuspend(mNativeMediaSessionDelegateAndroid, true); - break; - case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: - mIsDucking = true; - nativeRecordSessionDuck(mNativeMediaSessionDelegateAndroid); - nativeOnStartDucking(mNativeMediaSessionDelegateAndroid); - break; - case AudioManager.AUDIOFOCUS_LOSS: - abandonAudioFocus(); - nativeOnSuspend(mNativeMediaSessionDelegateAndroid, false); - break; - default: - Log.w(TAG, "onAudioFocusChange called with unexpected value %d", focusChange); - break; - } - } - - private native void nativeOnSuspend(long nativeMediaSessionDelegateAndroid, boolean temporary); - private native void nativeOnResume(long nativeMediaSessionDelegateAndroid); - private native void nativeOnStartDucking(long nativeMediaSessionDelegateAndroid); - private native void nativeOnStopDucking(long nativeMediaSessionDelegateAndroid); - private native void nativeRecordSessionDuck(long nativeMediaSessionDelegateAndroid); -}
diff --git a/content/public/android/java/src/org/chromium/content/browser/OWNERS b/content/public/android/java/src/org/chromium/content/browser/OWNERS index 1dbbbe30..fb4de38 100644 --- a/content/public/android/java/src/org/chromium/content/browser/OWNERS +++ b/content/public/android/java/src/org/chromium/content/browser/OWNERS
@@ -10,8 +10,8 @@ per-file ScreenOrientation*.java=mlamouri@chromium.org # Media Session API related -per-file MediaSession*.java=avayvod@chromium.org -per-file MediaSession*.java=mlamouri@chromium.org +per-file AudioFocus*.java=avayvod@chromium.org +per-file AudioFocus*.java=mlamouri@chromium.org # Process management related per-file BindingManager*.java=jaekyun@chromium.org
diff --git a/content/public/common/common_param_traits_macros.h b/content/public/common/common_param_traits_macros.h index 12193432..0f4b8f6 100644 --- a/content/public/common/common_param_traits_macros.h +++ b/content/public/common/common_param_traits_macros.h
@@ -152,7 +152,6 @@ IPC_STRUCT_TRAITS_MEMBER(pepper_3d_enabled) IPC_STRUCT_TRAITS_MEMBER(inert_visual_viewport) IPC_STRUCT_TRAITS_MEMBER(record_whole_document) - IPC_STRUCT_TRAITS_MEMBER(pinch_overlay_scrollbar_thickness) IPC_STRUCT_TRAITS_MEMBER(use_solid_color_scrollbars) IPC_STRUCT_TRAITS_MEMBER(flash_3d_enabled) IPC_STRUCT_TRAITS_MEMBER(flash_stage3d_enabled)
diff --git a/content/public/common/web_preferences.cc b/content/public/common/web_preferences.cc index c485914..29426a13 100644 --- a/content/public/common/web_preferences.cc +++ b/content/public/common/web_preferences.cc
@@ -171,7 +171,6 @@ smart_insert_delete_enabled(false), #endif spatial_navigation_enabled(false), - pinch_overlay_scrollbar_thickness(0), use_solid_color_scrollbars(false), navigate_on_drag_drop(true), v8_cache_options(V8_CACHE_OPTIONS_DEFAULT),
diff --git a/content/public/common/web_preferences.h b/content/public/common/web_preferences.h index 097acbc..bdceefc 100644 --- a/content/public/common/web_preferences.h +++ b/content/public/common/web_preferences.h
@@ -184,7 +184,6 @@ bool initialize_at_minimum_page_scale; bool smart_insert_delete_enabled; bool spatial_navigation_enabled; - int pinch_overlay_scrollbar_thickness; bool use_solid_color_scrollbars; bool navigate_on_drag_drop; V8CacheOptions v8_cache_options;
diff --git a/content/public/test/text_input_test_utils.h b/content/public/test/text_input_test_utils.h index 7785b67..080d0fa 100644 --- a/content/public/test/text_input_test_utils.h +++ b/content/public/test/text_input_test_utils.h
@@ -13,6 +13,14 @@ #include "ui/base/ime/text_input_mode.h" #include "ui/base/ime/text_input_type.h" +#ifdef OS_MACOSX +#include "content/public/browser/browser_message_filter.h" +#endif + +namespace ipc { +class Message; +} + namespace gfx { class Range; } @@ -23,6 +31,8 @@ namespace content { +class MessageLoopRunner; +class RenderProcessHost; class RenderWidgetHost; class RenderWidgetHostView; class RenderWidgetHostViewBase; @@ -180,6 +190,48 @@ TestInputMethodObserver(); }; +#ifdef OS_MACOSX +// The test message filter for TextInputClientMac incoming messages from the +// renderer. +// NOTE: This filter should be added to the intended RenderProcessHost before +// the actual TextInputClientMessageFilter, otherwise, the messages +// will be handled and will never receive this filter. +class TestTextInputClientMessageFilter : public BrowserMessageFilter { + public: + // Creates the filter and adds itself to |host|. + TestTextInputClientMessageFilter(RenderProcessHost* host); + + // Wait until the IPC TextInputClientReplyMsg_GotStringForRange arrives. + void WaitForStringFromRange(); + + // BrowserMessageFilter overrides. + bool OnMessageReceived(const IPC::Message& message) override; + + RenderProcessHost* process() const { return host_; } + std::string string_from_range() { return string_from_range_; } + + private: + ~TestTextInputClientMessageFilter() override; + RenderProcessHost* const host_; + std::string string_from_range_; + bool received_string_from_range_; + scoped_refptr<MessageLoopRunner> message_loop_runner_; + + DISALLOW_COPY_AND_ASSIGN(TestTextInputClientMessageFilter); +}; + +// Requests the |tab_view| for the definition of the word identified by the +// given selection range. |range| identifies a word in the focused +// RenderWidgetHost underneath |tab_view| which may be different than the +// RenderWidgetHost corresponding to |tab_view|. +void AskForLookUpDictionaryForRange(RenderWidgetHostView* tab_view, + const gfx::Range& range); + +// Returns the total count of NSWindows instances which belong to the currently +// running NSApplication. +size_t GetOpenNSWindowsCount(); +#endif + } // namespace content #endif // CONTENT_PUBLIC_TEST_TEXT_INPUT_TEST_UTILS_H_
diff --git a/content/public/test/text_input_test_utils_mac.mm b/content/public/test/text_input_test_utils_mac.mm new file mode 100644 index 0000000..29de863 --- /dev/null +++ b/content/public/test/text_input_test_utils_mac.mm
@@ -0,0 +1,78 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/public/test/text_input_test_utils.h" + +#import <Cocoa/Cocoa.h> + +#include "base/memory/ref_counted.h" +#include "base/strings/sys_string_conversions.h" +#include "content/browser/renderer_host/render_widget_host_view_mac.h" +#include "content/browser/site_instance_impl.h" +#include "content/common/mac/attributed_string_coder.h" +#include "content/common/text_input_client_messages.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/test/test_utils.h" +#include "ipc/ipc_message_macros.h" + +namespace content { + +TestTextInputClientMessageFilter::TestTextInputClientMessageFilter( + RenderProcessHost* host) + : BrowserMessageFilter(TextInputClientMsgStart), + host_(host), + received_string_from_range_(false) { + host->AddFilter(this); +} + +TestTextInputClientMessageFilter::~TestTextInputClientMessageFilter() {} + +void TestTextInputClientMessageFilter::WaitForStringFromRange() { + if (received_string_from_range_) + return; + message_loop_runner_ = new MessageLoopRunner(); + message_loop_runner_->Run(); +} + +bool TestTextInputClientMessageFilter::OnMessageReceived( + const IPC::Message& message) { + if (message.type() == TextInputClientReplyMsg_GotStringForRange::ID) { + received_string_from_range_ = true; + + // Now decode the string to get the word. + TextInputClientReplyMsg_GotStringForRange::Param params; + TextInputClientReplyMsg_GotStringForRange::Read(&message, ¶ms); + const mac::AttributedStringCoder::EncodedString& encoded_string = + std::get<0>(params); + NSAttributedString* decoded = + mac::AttributedStringCoder::Decode(&encoded_string); + string_from_range_ = base::SysNSStringToUTF8([decoded string]); + + // Stop the message loop if it is running. + if (message_loop_runner_) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + message_loop_runner_->QuitClosure()); + } + } + + // unhandled - leave it for the actual TextInputClientMessageFilter to handle. + return false; +} + +void AskForLookUpDictionaryForRange(RenderWidgetHostView* tab_view, + const gfx::Range& range) { + RenderWidgetHostViewCocoa* cocoa_view = + static_cast<RenderWidgetHostViewMac*>(tab_view)->cocoa_view(); + + // Explicitly ask for the dictionary for the given range. + [cocoa_view showLookUpDictionaryOverlayFromRange:range.ToNSRange() + targetView:cocoa_view]; +} + +size_t GetOpenNSWindowsCount() { + return [[NSApp windows] count]; +} + +} // namespace content
diff --git a/content/renderer/manifest/manifest_parser.h b/content/renderer/manifest/manifest_parser.h index bf5acad6..9355930 100644 --- a/content/renderer/manifest/manifest_parser.h +++ b/content/renderer/manifest/manifest_parser.h
@@ -80,59 +80,59 @@ const GURL& base_url); // Parses the 'name' field of the manifest, as defined in: - // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-name-member + // https://w3c.github.io/manifest/#dfn-steps-for-processing-the-name-member // Returns the parsed string if any, a null string if the parsing failed. base::NullableString16 ParseName(const base::DictionaryValue& dictionary); // Parses the 'short_name' field of the manifest, as defined in: - // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-short-name-member + // https://w3c.github.io/manifest/#dfn-steps-for-processing-the-short-name-member // Returns the parsed string if any, a null string if the parsing failed. base::NullableString16 ParseShortName( const base::DictionaryValue& dictionary); // Parses the 'scope' field of the manifest, as defined in: - // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-scope-member + // https://w3c.github.io/manifest/#dfn-steps-for-processing-the-scope-member // Returns the parsed GURL if any, an empty GURL if the parsing failed. GURL ParseScope(const base::DictionaryValue& dictionary, const GURL& start_url); // Parses the 'start_url' field of the manifest, as defined in: - // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-start_url-member + // https://w3c.github.io/manifest/#dfn-steps-for-processing-the-start_url-member // Returns the parsed GURL if any, an empty GURL if the parsing failed. GURL ParseStartURL(const base::DictionaryValue& dictionary); // Parses the 'display' field of the manifest, as defined in: - // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-display-member + // https://w3c.github.io/manifest/#dfn-steps-for-processing-the-display-member // Returns the parsed DisplayMode if any, WebDisplayModeUndefined if the // parsing failed. blink::WebDisplayMode ParseDisplay(const base::DictionaryValue& dictionary); // Parses the 'orientation' field of the manifest, as defined in: - // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-orientation-member + // https://w3c.github.io/manifest/#dfn-steps-for-processing-the-orientation-member // Returns the parsed WebScreenOrientationLockType if any, // WebScreenOrientationLockDefault if the parsing failed. blink::WebScreenOrientationLockType ParseOrientation( const base::DictionaryValue& dictionary); // Parses the 'src' field of an icon, as defined in: - // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-src-member-of-an-icon + // https://w3c.github.io/manifest/#dfn-steps-for-processing-the-src-member-of-an-image // Returns the parsed GURL if any, an empty GURL if the parsing failed. GURL ParseIconSrc(const base::DictionaryValue& icon); // Parses the 'type' field of an icon, as defined in: - // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-type-member-of-an-icon + // https://w3c.github.io/manifest/#dfn-steps-for-processing-the-type-member-of-an-image // Returns the parsed string if any, an empty string if the parsing failed. base::string16 ParseIconType(const base::DictionaryValue& icon); // Parses the 'sizes' field of an icon, as defined in: - // http://w3c.github.io/manifest/#dfn-steps-for-processing-a-sizes-member-of-an-icon + // https://w3c.github.io/manifest/#dfn-steps-for-processing-a-sizes-member-of-an-image // Returns a vector of gfx::Size with the successfully parsed sizes, if any. // An empty vector if the field was not present or empty. "Any" is represented // by gfx::Size(0, 0). std::vector<gfx::Size> ParseIconSizes(const base::DictionaryValue& icon); // Parses the 'icons' field of a Manifest, as defined in: - // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-icons-member + // https://w3c.github.io/manifest/#dfn-steps-for-processing-an-array-of-images // Returns a vector of Manifest::Icon with the successfully parsed icons, if // any. An empty vector if the field was not present or empty. std::vector<Manifest::Icon> ParseIcons( @@ -170,13 +170,13 @@ bool ParsePreferRelatedApplications(const base::DictionaryValue& dictionary); // Parses the 'theme_color' field of the manifest, as defined in: - // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-theme_color-member + // https://w3c.github.io/manifest/#dfn-steps-for-processing-the-theme_color-member // Returns the parsed theme color if any, // Manifest::kInvalidOrMissingColor if the parsing failed. int64_t ParseThemeColor(const base::DictionaryValue& dictionary); // Parses the 'background_color' field of the manifest, as defined in: - // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-background_color-member + // https://w3c.github.io/manifest/#dfn-steps-for-processing-the-background_color-member // Returns the parsed background color if any, // Manifest::kInvalidOrMissingColor if the parsing failed. int64_t ParseBackgroundColor(const base::DictionaryValue& dictionary);
diff --git a/content/renderer/media/gpu/rtc_video_encoder.cc b/content/renderer/media/gpu/rtc_video_encoder.cc index 34b920d9..9e7a12e 100644 --- a/content/renderer/media/gpu/rtc_video_encoder.cc +++ b/content/renderer/media/gpu/rtc_video_encoder.cc
@@ -752,7 +752,10 @@ << ", width=" << codec_settings->width << ", height=" << codec_settings->height << ", startBitrate=" << codec_settings->startBitrate; - DCHECK(!impl_.get()); + if (impl_) { + DVLOG(1) << "Release because of reinitialization"; + Release(); + } impl_ = new Impl(gpu_factories_, video_codec_type_); const media::VideoCodecProfile profile = WebRTCVideoCodecToVideoCodecProfile(
diff --git a/content/renderer/media/gpu/rtc_video_encoder_unittest.cc b/content/renderer/media/gpu/rtc_video_encoder_unittest.cc index 475d4c92..3cdab3f7 100644 --- a/content/renderer/media/gpu/rtc_video_encoder_unittest.cc +++ b/content/renderer/media/gpu/rtc_video_encoder_unittest.cc
@@ -46,19 +46,28 @@ idle_waiter_(base::WaitableEvent::ResetPolicy::AUTOMATIC, base::WaitableEvent::InitialState::NOT_SIGNALED) {} + media::MockVideoEncodeAccelerator* ExpectCreateInitAndDestroyVEA() { + // The VEA will be owned by the RTCVideoEncoder once + // factory.CreateVideoEncodeAccelerator() is called. + media::MockVideoEncodeAccelerator* mock_vea = + new media::MockVideoEncodeAccelerator(); + + EXPECT_CALL(*mock_gpu_factories_.get(), DoCreateVideoEncodeAccelerator()) + .WillRepeatedly(Return(mock_vea)); + EXPECT_CALL(*mock_vea, Initialize(_, _, _, _, _)) + .WillOnce(Invoke(this, &RTCVideoEncoderTest::Initialize)); + EXPECT_CALL(*mock_vea, UseOutputBitstreamBuffer(_)).Times(3); + EXPECT_CALL(*mock_vea, Destroy()).Times(1); + return mock_vea; + } + void SetUp() override { DVLOG(3) << __FUNCTION__; ASSERT_TRUE(encoder_thread_.Start()); - mock_vea_ = new media::MockVideoEncodeAccelerator(); EXPECT_CALL(*mock_gpu_factories_.get(), GetTaskRunner()) .WillRepeatedly(Return(encoder_thread_.task_runner())); - EXPECT_CALL(*mock_gpu_factories_.get(), DoCreateVideoEncodeAccelerator()) - .WillRepeatedly(Return(mock_vea_)); - EXPECT_CALL(*mock_vea_, Initialize(_, _, _, _, _)) - .WillOnce(Invoke(this, &RTCVideoEncoderTest::Initialize)); - EXPECT_CALL(*mock_vea_, UseOutputBitstreamBuffer(_)).Times(3); - EXPECT_CALL(*mock_vea_, Destroy()).Times(1); + mock_vea_ = ExpectCreateInitAndDestroyVEA(); } void TearDown() override { @@ -145,6 +154,17 @@ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_encoder_->InitEncode(&codec, 1, 12345)); } +TEST_P(RTCVideoEncoderTest, RepeatedInitSucceeds) { + const webrtc::VideoCodecType codec_type = GetParam(); + CreateEncoder(codec_type); + webrtc::VideoCodec codec = GetDefaultCodec(); + codec.codecType = codec_type; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_encoder_->InitEncode(&codec, 1, 12345)); + + ExpectCreateInitAndDestroyVEA(); + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_encoder_->InitEncode(&codec, 1, 12345)); +} + TEST_F(RTCVideoEncoderTest, EncodeScaledFrame) { const webrtc::VideoCodecType codec_type = webrtc::kVideoCodecVP8; CreateEncoder(codec_type);
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index 7dd11dd..0891c86 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc
@@ -1166,8 +1166,6 @@ settings->setMainFrameResizesAreOrientationChanges( prefs.main_frame_resizes_are_orientation_changes); - settings->setPinchOverlayScrollbarThickness( - prefs.pinch_overlay_scrollbar_thickness); settings->setUseSolidColorScrollbars(prefs.use_solid_color_scrollbars); settings->setShowContextMenuOnMouseUp(prefs.context_menu_on_mouse_up);
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index f6f8b8f..764d2af 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -111,6 +111,7 @@ "../public/test/test_web_ui.h", "../public/test/text_input_test_utils.cc", "../public/test/text_input_test_utils.h", + "../public/test/text_input_test_utils_mac.mm", "../public/test/unittest_test_suite.cc", "../public/test/unittest_test_suite.h", "../public/test/web_contents_tester.cc", @@ -580,11 +581,11 @@ "../browser/media/media_redirect_browsertest.cc", "../browser/media/media_source_browsertest.cc", "../browser/media/midi_browsertest.cc", + "../browser/media/session/audio_focus_delegate_default_browsertest.cc", "../browser/media/session/media_session_browsertest.cc", - "../browser/media/session/media_session_delegate_default_browsertest.cc", "../browser/media/session/media_session_visibility_browsertest.cc", - "../browser/media/session/mock_media_session_observer.cc", - "../browser/media/session/mock_media_session_observer.h", + "../browser/media/session/mock_media_session_player_observer.cc", + "../browser/media/session/mock_media_session_player_observer.h", "../browser/memory/memory_coordinator_browsertest.cc", "../browser/memory/memory_pressure_controller_impl_browsertest.cc", "../browser/message_port_provider_browsertest.cc", @@ -785,12 +786,12 @@ if (is_android) { sources += [ "../browser/accessibility/android_granularity_movement_browsertest.cc", - "../browser/media/session/media_session_delegate_android_browsertest.cc", + "../browser/media/session/audio_focus_delegate_android_browsertest.cc", "../shell/android/browsertests_apk/content_browser_tests_jni_onload.cc", ] sources -= [ "../browser/battery_status/battery_monitor_impl_browsertest.cc", - "../browser/media/session/media_session_delegate_default_browsertest.cc", + "../browser/media/session/audio_focus_delegate_default_browsertest.cc", ] deps += [ ":content_browsertests_java",
diff --git a/device/bluetooth/bluetooth_adapter.cc b/device/bluetooth/bluetooth_adapter.cc index 1701b80..86b3769 100644 --- a/device/bluetooth/bluetooth_adapter.cc +++ b/device/bluetooth/bluetooth_adapter.cc
@@ -166,23 +166,23 @@ } void BluetoothAdapter::NotifyAdapterPoweredChanged(bool powered) { - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - AdapterPoweredChanged(this, powered)); + for (auto& observer : observers_) + observer.AdapterPoweredChanged(this, powered); } void BluetoothAdapter::NotifyDeviceChanged(BluetoothDevice* device) { DCHECK(device); DCHECK_EQ(device->GetAdapter(), this); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceChanged(this, device)); + for (auto& observer : observers_) + observer.DeviceChanged(this, device); } #if defined(OS_CHROMEOS) || defined(OS_LINUX) void BluetoothAdapter::NotifyDevicePairedChanged(BluetoothDevice* device, bool new_paired_status) { - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DevicePairedChanged(this, device, new_paired_status)); + for (auto& observer : observers_) + observer.DevicePairedChanged(this, device, new_paired_status); } #endif @@ -190,55 +190,55 @@ BluetoothRemoteGattService* service) { DCHECK_EQ(service->GetDevice()->GetAdapter(), this); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - GattServiceAdded(this, service->GetDevice(), service)); + for (auto& observer : observers_) + observer.GattServiceAdded(this, service->GetDevice(), service); } void BluetoothAdapter::NotifyGattServiceRemoved( BluetoothRemoteGattService* service) { DCHECK_EQ(service->GetDevice()->GetAdapter(), this); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - GattServiceRemoved(this, service->GetDevice(), service)); + for (auto& observer : observers_) + observer.GattServiceRemoved(this, service->GetDevice(), service); } void BluetoothAdapter::NotifyGattServiceChanged( BluetoothRemoteGattService* service) { DCHECK_EQ(service->GetDevice()->GetAdapter(), this); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - GattServiceChanged(this, service)); + for (auto& observer : observers_) + observer.GattServiceChanged(this, service); } void BluetoothAdapter::NotifyGattServicesDiscovered(BluetoothDevice* device) { DCHECK(device->GetAdapter() == this); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - GattServicesDiscovered(this, device)); + for (auto& observer : observers_) + observer.GattServicesDiscovered(this, device); } void BluetoothAdapter::NotifyGattDiscoveryComplete( BluetoothRemoteGattService* service) { DCHECK_EQ(service->GetDevice()->GetAdapter(), this); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - GattDiscoveryCompleteForService(this, service)); + for (auto& observer : observers_) + observer.GattDiscoveryCompleteForService(this, service); } void BluetoothAdapter::NotifyGattCharacteristicAdded( BluetoothRemoteGattCharacteristic* characteristic) { DCHECK_EQ(characteristic->GetService()->GetDevice()->GetAdapter(), this); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - GattCharacteristicAdded(this, characteristic)); + for (auto& observer : observers_) + observer.GattCharacteristicAdded(this, characteristic); } void BluetoothAdapter::NotifyGattCharacteristicRemoved( BluetoothRemoteGattCharacteristic* characteristic) { DCHECK_EQ(characteristic->GetService()->GetDevice()->GetAdapter(), this); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - GattCharacteristicRemoved(this, characteristic)); + for (auto& observer : observers_) + observer.GattCharacteristicRemoved(this, characteristic); } void BluetoothAdapter::NotifyGattDescriptorAdded( @@ -247,8 +247,8 @@ descriptor->GetCharacteristic()->GetService()->GetDevice()->GetAdapter(), this); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - GattDescriptorAdded(this, descriptor)); + for (auto& observer : observers_) + observer.GattDescriptorAdded(this, descriptor); } void BluetoothAdapter::NotifyGattDescriptorRemoved( @@ -257,8 +257,8 @@ descriptor->GetCharacteristic()->GetService()->GetDevice()->GetAdapter(), this); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - GattDescriptorRemoved(this, descriptor)); + for (auto& observer : observers_) + observer.GattDescriptorRemoved(this, descriptor); } void BluetoothAdapter::NotifyGattCharacteristicValueChanged( @@ -266,9 +266,8 @@ const std::vector<uint8_t>& value) { DCHECK_EQ(characteristic->GetService()->GetDevice()->GetAdapter(), this); - FOR_EACH_OBSERVER( - BluetoothAdapter::Observer, observers_, - GattCharacteristicValueChanged(this, characteristic, value)); + for (auto& observer : observers_) + observer.GattCharacteristicValueChanged(this, characteristic, value); } void BluetoothAdapter::NotifyGattDescriptorValueChanged( @@ -278,8 +277,8 @@ descriptor->GetCharacteristic()->GetService()->GetDevice()->GetAdapter(), this); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - GattDescriptorValueChanged(this, descriptor, value)); + for (auto& observer : observers_) + observer.GattDescriptorValueChanged(this, descriptor, value); } BluetoothAdapter::BluetoothAdapter() : weak_ptr_factory_(this) { @@ -399,8 +398,8 @@ devices_.take_and_erase(it); it = next; - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceRemoved(this, removed_device.get())); + for (auto& observer : observers_) + observer.DeviceRemoved(this, removed_device.get()); } }
diff --git a/device/bluetooth/bluetooth_adapter_android.cc b/device/bluetooth/bluetooth_adapter_android.cc index b4ab2ac..41228bb 100644 --- a/device/bluetooth/bluetooth_adapter_android.cc +++ b/device/bluetooth/bluetooth_adapter_android.cc
@@ -220,11 +220,11 @@ if (is_new_device) { devices_.add(device_address, std::move(device_android_owner)); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceAdded(this, device_android)); + for (auto& observer : observers_) + observer.DeviceAdded(this, device_android); } else { - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceChanged(this, device_android)); + for (auto& observer : observers_) + observer.DeviceChanged(this, device_android); } }
diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm index e383f9d..16a876e 100644 --- a/device/bluetooth/bluetooth_adapter_mac.mm +++ b/device/bluetooth/bluetooth_adapter_mac.mm
@@ -234,9 +234,8 @@ num_discovery_sessions_ = 0; MarkDiscoverySessionsAsInactive(); } - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, - observers_, - AdapterDiscoveringChanged(this, false)); + for (auto& observer : observers_) + observer.AdapterDiscoveringChanged(this, false); } void BluetoothAdapterMac::DeviceConnected(IOBluetoothDevice* device) { @@ -298,9 +297,8 @@ DVLOG(1) << "Added a discovery session"; num_discovery_sessions_++; - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, - observers_, - AdapterDiscoveringChanged(this, true)); + for (auto& observer : observers_) + observer.AdapterDiscoveringChanged(this, true); callback.Run(); } @@ -431,8 +429,8 @@ FROM_HERE_WITH_EXPLICIT_FUNCTION( "461181 BluetoothAdapterMac::PollAdapter::AdapterPresentChanged")); if (was_present != is_present) { - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - AdapterPresentChanged(this, is_present)); + for (auto& observer : observers_) + observer.AdapterPresentChanged(this, is_present); } // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181 @@ -442,8 +440,8 @@ "461181 BluetoothAdapterMac::PollAdapter::AdapterPowerChanged")); if (classic_powered_ != classic_powered) { classic_powered_ = classic_powered; - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - AdapterPoweredChanged(this, classic_powered_)); + for (auto& observer : observers_) + observer.AdapterPoweredChanged(this, classic_powered_); } // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181 @@ -484,8 +482,8 @@ devices_.set(device_address, base::WrapUnique(device_classic)); VLOG(1) << "Adding new classic device: " << device_classic->GetAddress(); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceAdded(this, device_classic)); + for (auto& observer : observers_) + observer.DeviceAdded(this, device_classic); } void BluetoothAdapterMac::LowEnergyDeviceUpdated( @@ -562,11 +560,11 @@ std::string device_address = BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral); devices_.add(device_address, std::unique_ptr<BluetoothDevice>(device_mac)); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceAdded(this, device_mac)); + for (auto& observer : observers_) + observer.DeviceAdded(this, device_mac); } else { - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceChanged(this, device_mac)); + for (auto& observer : observers_) + observer.DeviceChanged(this, device_mac); } }
diff --git a/device/bluetooth/bluetooth_adapter_win.cc b/device/bluetooth/bluetooth_adapter_win.cc index e440fec..1bacaac 100644 --- a/device/bluetooth/bluetooth_adapter_win.cc +++ b/device/bluetooth/bluetooth_adapter_win.cc
@@ -128,8 +128,8 @@ on_start_discovery_callbacks_.clear(); if (success) { - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - AdapterDiscoveringChanged(this, true)); + for (auto& observer : observers_) + observer.AdapterDiscoveringChanged(this, true); // If there are stop discovery requests, post the stop discovery again. MaybePostStopDiscoveryTask(); @@ -153,8 +153,8 @@ num_discovery_listeners_ = 0; on_stop_discovery_callbacks_.clear(); if (was_discovering) - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - AdapterDiscoveringChanged(this, false)); + for (auto& observer : observers_) + observer.AdapterDiscoveringChanged(this, false); // If there are start discovery requests, post the start discovery again. MaybePostStartDiscoveryTask(); @@ -212,13 +212,13 @@ bool is_present = !state.address.empty(); address_ = BluetoothDevice::CanonicalizeAddress(state.address); if (was_present != is_present) { - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - AdapterPresentChanged(this, is_present)); + for (auto& observer : observers_) + observer.AdapterPresentChanged(this, is_present); } if (powered_ != state.powered) { powered_ = state.powered; - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - AdapterPoweredChanged(this, powered_)); + for (auto& observer : observers_) + observer.AdapterPoweredChanged(this, powered_); } if (!initialized_) { initialized_ = true; @@ -258,8 +258,8 @@ ++iter) { std::unique_ptr<BluetoothDevice> device_win = devices_.take_and_erase(*iter); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceRemoved(this, device_win.get())); + for (auto& observer : observers_) + observer.DeviceRemoved(this, device_win.get()); } // Process added and (maybe) changed devices in one pass @@ -278,9 +278,8 @@ socket_thread_, NULL, net::NetLogSource()); devices_.set(device_state->address, std::unique_ptr<BluetoothDevice>(device_win)); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, - observers_, - DeviceAdded(this, device_win)); + for (auto& observer : observers_) + observer.DeviceAdded(this, device_win); } else if (changed_devices.find(device_state->address) != changed_devices.end()) { DevicesMap::const_iterator iter = devices_.find(device_state->address); @@ -289,8 +288,8 @@ static_cast<BluetoothDeviceWin*>(iter->second); if (!device_win->IsEqual(*device_state)) { device_win->Update(*device_state); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceChanged(this, device_win)); + for (auto& observer : observers_) + observer.DeviceChanged(this, device_win); } // Above IsEqual returns true if device name, address, status and services // (primary services of BLE device) are the same. However, in BLE tests,
diff --git a/device/bluetooth/bluetooth_task_manager_win.cc b/device/bluetooth/bluetooth_task_manager_win.cc index df0b0fc..4573fb9 100644 --- a/device/bluetooth/bluetooth_task_manager_win.cc +++ b/device/bluetooth/bluetooth_task_manager_win.cc
@@ -361,27 +361,27 @@ void BluetoothTaskManagerWin::OnAdapterStateChanged(const AdapterState* state) { DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); - FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_, - AdapterStateChanged(*state)); + for (auto& observer : observers_) + observer.AdapterStateChanged(*state); } void BluetoothTaskManagerWin::OnDiscoveryStarted(bool success) { DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); - FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_, - DiscoveryStarted(success)); + for (auto& observer : observers_) + observer.DiscoveryStarted(success); } void BluetoothTaskManagerWin::OnDiscoveryStopped() { DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); - FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_, - DiscoveryStopped()); + for (auto& observer : observers_) + observer.DiscoveryStopped(); } void BluetoothTaskManagerWin::OnDevicesPolled( const ScopedVector<DeviceState>* devices) { DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); - FOR_EACH_OBSERVER( - BluetoothTaskManagerWin::Observer, observers_, DevicesPolled(*devices)); + for (auto& observer : observers_) + observer.DevicesPolled(*devices); } void BluetoothTaskManagerWin::PollAdapter() {
diff --git a/device/bluetooth/bluez/bluetooth_adapter_bluez.cc b/device/bluetooth/bluez/bluetooth_adapter_bluez.cc index ad4510a..6377518b 100644 --- a/device/bluetooth/bluez/bluetooth_adapter_bluez.cc +++ b/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
@@ -539,8 +539,8 @@ devices_.set(device_bluez->GetAddress(), std::unique_ptr<BluetoothDevice>(device_bluez)); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceAdded(this, device_bluez)); + for (auto& observer : observers_) + observer.DeviceAdded(this, device_bluez); } void BluetoothAdapterBlueZ::DeviceRemoved(const dbus::ObjectPath& object_path) { @@ -552,8 +552,8 @@ std::unique_ptr<BluetoothDevice> scoped_device = devices_.take_and_erase(iter->first); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceRemoved(this, device_bluez)); + for (auto& observer : observers_) + observer.DeviceRemoved(this, device_bluez); return; } } @@ -1004,16 +1004,16 @@ devices_swapped.swap(devices_); for (auto& iter : devices_swapped) { - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceRemoved(this, iter.second)); + for (auto& observer : observers_) + observer.DeviceRemoved(this, iter.second); } PresentChanged(false); } void BluetoothAdapterBlueZ::DiscoverableChanged(bool discoverable) { - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - AdapterDiscoverableChanged(this, discoverable)); + for (auto& observer : observers_) + observer.AdapterDiscoverableChanged(this, discoverable); } void BluetoothAdapterBlueZ::DiscoveringChanged(bool discovering) { @@ -1026,13 +1026,13 @@ num_discovery_sessions_ = 0; MarkDiscoverySessionsAsInactive(); } - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - AdapterDiscoveringChanged(this, discovering)); + for (auto& observer : observers_) + observer.AdapterDiscoveringChanged(this, discovering); } void BluetoothAdapterBlueZ::PresentChanged(bool present) { - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - AdapterPresentChanged(this, present)); + for (auto& observer : observers_) + observer.AdapterPresentChanged(this, present); } void BluetoothAdapterBlueZ::NotifyDeviceAddressChanged( @@ -1040,8 +1040,8 @@ const std::string& old_address) { DCHECK(device->adapter_ == this); - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceAddressChanged(this, device, old_address)); + for (auto& observer : observers_) + observer.DeviceAddressChanged(this, device, old_address); } void BluetoothAdapterBlueZ::UseProfile(
diff --git a/device/bluetooth/bluez/bluetooth_advertisement_bluez.cc b/device/bluetooth/bluez/bluetooth_advertisement_bluez.cc index d03fd95..0adece73 100644 --- a/device/bluetooth/bluez/bluetooth_advertisement_bluez.cc +++ b/device/bluetooth/bluez/bluetooth_advertisement_bluez.cc
@@ -130,8 +130,8 @@ void BluetoothAdvertisementBlueZ::Released() { LOG(WARNING) << "Advertisement released."; provider_.reset(); - FOR_EACH_OBSERVER(BluetoothAdvertisement::Observer, observers_, - AdvertisementReleased(this)); + for (auto& observer : observers_) + observer.AdvertisementReleased(this); } } // namespace bluez
diff --git a/device/bluetooth/dbus/bluetooth_adapter_client.cc b/device/bluetooth/dbus/bluetooth_adapter_client.cc index d763696d..e948e6aa 100644 --- a/device/bluetooth/dbus/bluetooth_adapter_client.cc +++ b/device/bluetooth/dbus/bluetooth_adapter_client.cc
@@ -426,16 +426,16 @@ // is created. Informs observers. void ObjectAdded(const dbus::ObjectPath& object_path, const std::string& interface_name) override { - FOR_EACH_OBSERVER(BluetoothAdapterClient::Observer, observers_, - AdapterAdded(object_path)); + for (auto& observer : observers_) + observer.AdapterAdded(object_path); } // Called by dbus::ObjectManager when an object with the adapter interface // is removed. Informs observers. void ObjectRemoved(const dbus::ObjectPath& object_path, const std::string& interface_name) override { - FOR_EACH_OBSERVER(BluetoothAdapterClient::Observer, observers_, - AdapterRemoved(object_path)); + for (auto& observer : observers_) + observer.AdapterRemoved(object_path); } // Called by dbus::PropertySet when a property value is changed, @@ -443,8 +443,8 @@ // call. Informs observers. void OnPropertyChanged(const dbus::ObjectPath& object_path, const std::string& property_name) { - FOR_EACH_OBSERVER(BluetoothAdapterClient::Observer, observers_, - AdapterPropertyChanged(object_path, property_name)); + for (auto& observer : observers_) + observer.AdapterPropertyChanged(object_path, property_name); } // Called when a response for successful method call is received.
diff --git a/device/bluetooth/dbus/bluetooth_device_client.cc b/device/bluetooth/dbus/bluetooth_device_client.cc index 7093089..1eec1ca 100644 --- a/device/bluetooth/dbus/bluetooth_device_client.cc +++ b/device/bluetooth/dbus/bluetooth_device_client.cc
@@ -465,16 +465,16 @@ // is created. Informs observers. void ObjectAdded(const dbus::ObjectPath& object_path, const std::string& interface_name) override { - FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, - DeviceAdded(object_path)); + for (auto& observer : observers_) + observer.DeviceAdded(object_path); } // Called by dbus::ObjectManager when an object with the device interface // is removed. Informs observers. void ObjectRemoved(const dbus::ObjectPath& object_path, const std::string& interface_name) override { - FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, - DeviceRemoved(object_path)); + for (auto& observer : observers_) + observer.DeviceRemoved(object_path); } // Called by BluetoothPropertySet when a property value is changed, @@ -482,8 +482,8 @@ // call. Informs observers. void OnPropertyChanged(const dbus::ObjectPath& object_path, const std::string& property_name) { - FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, - DevicePropertyChanged(object_path, property_name)); + for (auto& observer : observers_) + observer.DevicePropertyChanged(object_path, property_name); } // Called when a response for successful method call is received.
diff --git a/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc b/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc index 1ce7c35b..7423eb6 100644 --- a/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc +++ b/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc
@@ -202,16 +202,16 @@ void ObjectAdded(const dbus::ObjectPath& object_path, const std::string& interface_name) override { VLOG(2) << "Remote GATT characteristic added: " << object_path.value(); - FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, - GattCharacteristicAdded(object_path)); + for (auto& observer : observers_) + observer.GattCharacteristicAdded(object_path); } // dbus::ObjectManager::Interface override. void ObjectRemoved(const dbus::ObjectPath& object_path, const std::string& interface_name) override { VLOG(2) << "Remote GATT characteristic removed: " << object_path.value(); - FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, - GattCharacteristicRemoved(object_path)); + for (auto& observer : observers_) + observer.GattCharacteristicRemoved(object_path); } protected: @@ -234,9 +234,8 @@ const std::string& property_name) { VLOG(2) << "Remote GATT characteristic property changed: " << object_path.value() << ": " << property_name; - FOR_EACH_OBSERVER( - BluetoothGattCharacteristicClient::Observer, observers_, - GattCharacteristicPropertyChanged(object_path, property_name)); + for (auto& observer : observers_) + observer.GattCharacteristicPropertyChanged(object_path, property_name); } // Called when a response for successful method call is received.
diff --git a/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.cc b/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.cc index e4b724c..d083dee 100644 --- a/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.cc +++ b/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.cc
@@ -160,16 +160,16 @@ void ObjectAdded(const dbus::ObjectPath& object_path, const std::string& interface_name) override { VLOG(2) << "Remote GATT descriptor added: " << object_path.value(); - FOR_EACH_OBSERVER(BluetoothGattDescriptorClient::Observer, observers_, - GattDescriptorAdded(object_path)); + for (auto& observer : observers_) + observer.GattDescriptorAdded(object_path); } // dbus::ObjectManager::Interface override. void ObjectRemoved(const dbus::ObjectPath& object_path, const std::string& interface_name) override { VLOG(2) << "Remote GATT descriptor removed: " << object_path.value(); - FOR_EACH_OBSERVER(BluetoothGattDescriptorClient::Observer, observers_, - GattDescriptorRemoved(object_path)); + for (auto& observer : observers_) + observer.GattDescriptorRemoved(object_path); } protected: @@ -191,9 +191,8 @@ const std::string& property_name) { VLOG(2) << "Remote GATT descriptor property changed: " << object_path.value() << ": " << property_name; - FOR_EACH_OBSERVER( - BluetoothGattDescriptorClient::Observer, observers_, - GattDescriptorPropertyChanged(object_path, property_name)); + for (auto& observer : observers_) + observer.GattDescriptorPropertyChanged(object_path, property_name); } // Called when a response for a successful method call is received.
diff --git a/device/bluetooth/dbus/bluetooth_gatt_service_client.cc b/device/bluetooth/dbus/bluetooth_gatt_service_client.cc index bcc0fa8c..c27284f 100644 --- a/device/bluetooth/dbus/bluetooth_gatt_service_client.cc +++ b/device/bluetooth/dbus/bluetooth_gatt_service_client.cc
@@ -81,16 +81,16 @@ void ObjectAdded(const dbus::ObjectPath& object_path, const std::string& interface_name) override { VLOG(2) << "Remote GATT service added: " << object_path.value(); - FOR_EACH_OBSERVER(BluetoothGattServiceClient::Observer, observers_, - GattServiceAdded(object_path)); + for (auto& observer : observers_) + observer.GattServiceAdded(object_path); } // dbus::ObjectManager::Interface override. void ObjectRemoved(const dbus::ObjectPath& object_path, const std::string& interface_name) override { VLOG(2) << "Remote GATT service removed: " << object_path.value(); - FOR_EACH_OBSERVER(BluetoothGattServiceClient::Observer, observers_, - GattServiceRemoved(object_path)); + for (auto& observer : observers_) + observer.GattServiceRemoved(object_path); } protected: @@ -112,8 +112,8 @@ const std::string& property_name) { VLOG(2) << "Remote GATT service property changed: " << object_path.value() << ": " << property_name; - FOR_EACH_OBSERVER(BluetoothGattServiceClient::Observer, observers_, - GattServicePropertyChanged(object_path, property_name)); + for (auto& observer : observers_) + observer.GattServicePropertyChanged(object_path, property_name); } dbus::ObjectManager* object_manager_;
diff --git a/device/bluetooth/dbus/bluetooth_input_client.cc b/device/bluetooth/dbus/bluetooth_input_client.cc index a116e7b..de5bdd04 100644 --- a/device/bluetooth/dbus/bluetooth_input_client.cc +++ b/device/bluetooth/dbus/bluetooth_input_client.cc
@@ -83,16 +83,16 @@ // is created. Informs observers. void ObjectAdded(const dbus::ObjectPath& object_path, const std::string& interface_name) override { - FOR_EACH_OBSERVER(BluetoothInputClient::Observer, observers_, - InputAdded(object_path)); + for (auto& observer : observers_) + observer.InputAdded(object_path); } // Called by dbus::ObjectManager when an object with the input interface // is removed. Informs observers. void ObjectRemoved(const dbus::ObjectPath& object_path, const std::string& interface_name) override { - FOR_EACH_OBSERVER(BluetoothInputClient::Observer, observers_, - InputRemoved(object_path)); + for (auto& observer : observers_) + observer.InputRemoved(object_path); } // Called by BluetoothPropertySet when a property value is changed, @@ -100,8 +100,8 @@ // call. Informs observers. void OnPropertyChanged(const dbus::ObjectPath& object_path, const std::string& property_name) { - FOR_EACH_OBSERVER(BluetoothInputClient::Observer, observers_, - InputPropertyChanged(object_path, property_name)); + for (auto& observer : observers_) + observer.InputPropertyChanged(object_path, property_name); } dbus::ObjectManager* object_manager_;
diff --git a/device/bluetooth/dbus/bluetooth_le_advertising_manager_client.cc b/device/bluetooth/dbus/bluetooth_le_advertising_manager_client.cc index fcb39553..3488cc19 100644 --- a/device/bluetooth/dbus/bluetooth_le_advertising_manager_client.cc +++ b/device/bluetooth/dbus/bluetooth_le_advertising_manager_client.cc
@@ -150,16 +150,16 @@ // interface is created. Informs observers. void ObjectAdded(const dbus::ObjectPath& object_path, const std::string& interface_name) override { - FOR_EACH_OBSERVER(BluetoothLEAdvertisingManagerClient::Observer, observers_, - AdvertisingManagerAdded(object_path)); + for (auto& observer : observers_) + observer.AdvertisingManagerAdded(object_path); } // Called by dbus::ObjectManager when an object with the advertising manager // interface is removed. Informs observers. void ObjectRemoved(const dbus::ObjectPath& object_path, const std::string& interface_name) override { - FOR_EACH_OBSERVER(BluetoothLEAdvertisingManagerClient::Observer, observers_, - AdvertisingManagerRemoved(object_path)); + for (auto& observer : observers_) + observer.AdvertisingManagerRemoved(object_path); } // Called when a response for successful method call is received.
diff --git a/device/bluetooth/dbus/bluetooth_media_client.cc b/device/bluetooth/dbus/bluetooth_media_client.cc index 3815bc87..3f6aa401 100644 --- a/device/bluetooth/dbus/bluetooth_media_client.cc +++ b/device/bluetooth/dbus/bluetooth_media_client.cc
@@ -73,15 +73,15 @@ void ObjectAdded(const dbus::ObjectPath& object_path, const std::string& interface_name) override { VLOG(1) << "Remote Media added: " << object_path.value(); - FOR_EACH_OBSERVER(BluetoothMediaClient::Observer, observers_, - MediaAdded(object_path)); + for (auto& observer : observers_) + observer.MediaAdded(object_path); } void ObjectRemoved(const dbus::ObjectPath& object_path, const std::string& interface_name) override { VLOG(1) << "Remote Media removed: " << object_path.value(); - FOR_EACH_OBSERVER(BluetoothMediaClient::Observer, observers_, - MediaRemoved(object_path)); + for (auto& observer : observers_) + observer.MediaRemoved(object_path); } // BluetoothMediaClient overrides.
diff --git a/device/bluetooth/dbus/bluetooth_media_transport_client.cc b/device/bluetooth/dbus/bluetooth_media_transport_client.cc index 774d5e97..d6eb950 100644 --- a/device/bluetooth/dbus/bluetooth_media_transport_client.cc +++ b/device/bluetooth/dbus/bluetooth_media_transport_client.cc
@@ -92,15 +92,15 @@ void ObjectAdded(const dbus::ObjectPath& object_path, const std::string& interface_name) override { VLOG(1) << "Remote Media Transport added: " << object_path.value(); - FOR_EACH_OBSERVER(BluetoothMediaTransportClient::Observer, observers_, - MediaTransportAdded(object_path)); + for (auto& observer : observers_) + observer.MediaTransportAdded(object_path); } void ObjectRemoved(const dbus::ObjectPath& object_path, const std::string& interface_name) override { VLOG(1) << "Remote Media Transport removed: " << object_path.value(); - FOR_EACH_OBSERVER(BluetoothMediaTransportClient::Observer, observers_, - MediaTransportRemoved(object_path)); + for (auto& observer : observers_) + observer.MediaTransportRemoved(object_path); } // BluetoothMediaTransportClient overrides. @@ -206,9 +206,8 @@ VLOG(1) << "Name of the changed property: " << property_name; // Dispatches the change to the corresponding property-changed handler. - FOR_EACH_OBSERVER( - BluetoothMediaTransportClient::Observer, observers_, - MediaTransportPropertyChanged(object_path, property_name)); + for (auto& observer : observers_) + observer.MediaTransportPropertyChanged(object_path, property_name); } // Called when a response for successful method call is received.
diff --git a/device/bluetooth/dbus/fake_bluetooth_adapter_client.cc b/device/bluetooth/dbus/fake_bluetooth_adapter_client.cc index cc8ce7d3..8d4478c 100644 --- a/device/bluetooth/dbus/fake_bluetooth_adapter_client.cc +++ b/device/bluetooth/dbus/fake_bluetooth_adapter_client.cc
@@ -274,15 +274,15 @@ // Adapter becoming visible visible_ = visible; - FOR_EACH_OBSERVER(BluetoothAdapterClient::Observer, observers_, - AdapterAdded(dbus::ObjectPath(kAdapterPath))); + for (auto& observer : observers_) + observer.AdapterAdded(dbus::ObjectPath(kAdapterPath)); } else if (visible_ && !visible) { // Adapter becoming invisible visible_ = visible; - FOR_EACH_OBSERVER(BluetoothAdapterClient::Observer, observers_, - AdapterRemoved(dbus::ObjectPath(kAdapterPath))); + for (auto& observer : observers_) + observer.AdapterRemoved(dbus::ObjectPath(kAdapterPath)); } } @@ -291,15 +291,15 @@ // Second adapter becoming visible second_visible_ = visible; - FOR_EACH_OBSERVER(BluetoothAdapterClient::Observer, observers_, - AdapterAdded(dbus::ObjectPath(kSecondAdapterPath))); + for (auto& observer : observers_) + observer.AdapterAdded(dbus::ObjectPath(kSecondAdapterPath)); } else if (second_visible_ && !visible) { // Second adapter becoming invisible second_visible_ = visible; - FOR_EACH_OBSERVER(BluetoothAdapterClient::Observer, observers_, - AdapterRemoved(dbus::ObjectPath(kSecondAdapterPath))); + for (auto& observer : observers_) + observer.AdapterRemoved(dbus::ObjectPath(kSecondAdapterPath)); } } @@ -329,9 +329,10 @@ } } - FOR_EACH_OBSERVER( - BluetoothAdapterClient::Observer, observers_, - AdapterPropertyChanged(dbus::ObjectPath(kAdapterPath), property_name)); + for (auto& observer : observers_) { + observer.AdapterPropertyChanged(dbus::ObjectPath(kAdapterPath), + property_name); + } } void FakeBluetoothAdapterClient::PostDelayedTask(
diff --git a/device/bluetooth/dbus/fake_bluetooth_device_client.cc b/device/bluetooth/dbus/fake_bluetooth_device_client.cc index 76875d1b..4d86797 100644 --- a/device/bluetooth/dbus/fake_bluetooth_device_client.cc +++ b/device/bluetooth/dbus/fake_bluetooth_device_client.cc
@@ -765,8 +765,8 @@ properties_map_.insert(std::make_pair(device_path, std::move(properties))); device_list_.push_back(device_path); - FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, - DeviceAdded(device_path)); + for (auto& observer : observers_) + observer.DeviceAdded(device_path); } void FakeBluetoothDeviceClient::CreateDeviceWithProperties( @@ -799,8 +799,8 @@ properties_map_.insert(std::make_pair(device_path, std::move(properties))); device_list_.push_back(device_path); pairing_options_map_.insert(std::make_pair(device_path, std::move(options))); - FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, - DeviceAdded(device_path)); + for (auto& observer : observers_) + observer.DeviceAdded(device_path); } std::unique_ptr<base::ListValue> @@ -1076,8 +1076,8 @@ gatt_service_client->HideHeartRateService(); } - FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, - DeviceRemoved(device_path)); + for (auto& observer : observers_) + observer.DeviceRemoved(device_path); properties_map_.erase(iter); PairingOptionsMap::const_iterator options_iter = @@ -1093,8 +1093,8 @@ const std::string& property_name) { VLOG(2) << "Fake Bluetooth device property changed: " << object_path.value() << ": " << property_name; - FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, - DevicePropertyChanged(object_path, property_name)); + for (auto& observer : observers_) + observer.DevicePropertyChanged(object_path, property_name); } void FakeBluetoothDeviceClient::DiscoverySimulationTimer() { @@ -1790,8 +1790,8 @@ properties_map_.insert(std::make_pair(device_path, std::move(properties))); device_list_.push_back(device_path); - FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, - DeviceAdded(device_path)); + for (auto& observer : observers_) + observer.DeviceAdded(device_path); } } // namespace bluez
diff --git a/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc b/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc index 5953c7e8..e677bcf 100644 --- a/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc +++ b/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc
@@ -454,23 +454,22 @@ VLOG(2) << "Characteristic property changed: " << object_path.value() << ": " << property_name; - FOR_EACH_OBSERVER( - BluetoothGattCharacteristicClient::Observer, observers_, - GattCharacteristicPropertyChanged(object_path, property_name)); + for (auto& observer : observers_) + observer.GattCharacteristicPropertyChanged(object_path, property_name); } void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicAdded( const dbus::ObjectPath& object_path) { VLOG(2) << "GATT characteristic added: " << object_path.value(); - FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, - GattCharacteristicAdded(object_path)); + for (auto& observer : observers_) + observer.GattCharacteristicAdded(object_path); } void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicRemoved( const dbus::ObjectPath& object_path) { VLOG(2) << "GATT characteristic removed: " << object_path.value(); - FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, - GattCharacteristicRemoved(object_path)); + for (auto& observer : observers_) + observer.GattCharacteristicRemoved(object_path); } void FakeBluetoothGattCharacteristicClient::
diff --git a/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.cc b/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.cc index b32befc..92dd12a 100644 --- a/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.cc +++ b/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.cc
@@ -196,20 +196,20 @@ VLOG(2) << "Descriptor property changed: " << object_path.value() << ": " << property_name; - FOR_EACH_OBSERVER(BluetoothGattDescriptorClient::Observer, observers_, - GattDescriptorPropertyChanged(object_path, property_name)); + for (auto& observer : observers_) + observer.GattDescriptorPropertyChanged(object_path, property_name); } void FakeBluetoothGattDescriptorClient::NotifyDescriptorAdded( const dbus::ObjectPath& object_path) { - FOR_EACH_OBSERVER(BluetoothGattDescriptorClient::Observer, observers_, - GattDescriptorAdded(object_path)); + for (auto& observer : observers_) + observer.GattDescriptorAdded(object_path); } void FakeBluetoothGattDescriptorClient::NotifyDescriptorRemoved( const dbus::ObjectPath& object_path) { - FOR_EACH_OBSERVER(BluetoothGattDescriptorClient::Observer, observers_, - GattDescriptorRemoved(object_path)); + for (auto& observer : observers_) + observer.GattDescriptorRemoved(object_path); } } // namespace bluez
diff --git a/device/bluetooth/dbus/fake_bluetooth_gatt_service_client.cc b/device/bluetooth/dbus/fake_bluetooth_gatt_service_client.cc index 3c5591d..8ee318b 100644 --- a/device/bluetooth/dbus/fake_bluetooth_gatt_service_client.cc +++ b/device/bluetooth/dbus/fake_bluetooth_gatt_service_client.cc
@@ -181,22 +181,22 @@ const std::string& property_name) { VLOG(2) << "Fake GATT Service property changed: " << object_path.value() << ": " << property_name; - FOR_EACH_OBSERVER(BluetoothGattServiceClient::Observer, observers_, - GattServicePropertyChanged(object_path, property_name)); + for (auto& observer : observers_) + observer.GattServicePropertyChanged(object_path, property_name); } void FakeBluetoothGattServiceClient::NotifyServiceAdded( const dbus::ObjectPath& object_path) { VLOG(2) << "GATT service added: " << object_path.value(); - FOR_EACH_OBSERVER(BluetoothGattServiceClient::Observer, observers_, - GattServiceAdded(object_path)); + for (auto& observer : observers_) + observer.GattServiceAdded(object_path); } void FakeBluetoothGattServiceClient::NotifyServiceRemoved( const dbus::ObjectPath& object_path) { VLOG(2) << "GATT service removed: " << object_path.value(); - FOR_EACH_OBSERVER(BluetoothGattServiceClient::Observer, observers_, - GattServiceRemoved(object_path)); + for (auto& observer : observers_) + observer.GattServiceRemoved(object_path); } void FakeBluetoothGattServiceClient::ExposeHeartRateCharacteristics() {
diff --git a/device/bluetooth/dbus/fake_bluetooth_input_client.cc b/device/bluetooth/dbus/fake_bluetooth_input_client.cc index bd3b86f6..36030626 100644 --- a/device/bluetooth/dbus/fake_bluetooth_input_client.cc +++ b/device/bluetooth/dbus/fake_bluetooth_input_client.cc
@@ -95,8 +95,8 @@ properties_map_[object_path] = properties; - FOR_EACH_OBSERVER(BluetoothInputClient::Observer, observers_, - InputAdded(object_path)); + for (auto& observer : observers_) + observer.InputAdded(object_path); } void FakeBluetoothInputClient::RemoveInputDevice( @@ -106,8 +106,8 @@ if (it == properties_map_.end()) return; - FOR_EACH_OBSERVER(BluetoothInputClient::Observer, observers_, - InputRemoved(object_path)); + for (auto& observer : observers_) + observer.InputRemoved(object_path); delete it->second; properties_map_.erase(it); @@ -116,8 +116,8 @@ void FakeBluetoothInputClient::OnPropertyChanged( const dbus::ObjectPath& object_path, const std::string& property_name) { - FOR_EACH_OBSERVER(BluetoothInputClient::Observer, observers_, - InputPropertyChanged(object_path, property_name)); + for (auto& observer : observers_) + observer.InputPropertyChanged(object_path, property_name); } } // namespace bluez
diff --git a/device/bluetooth/dbus/fake_bluetooth_media_client.cc b/device/bluetooth/dbus/fake_bluetooth_media_client.cc index 3b7a83b9..7ca3a7a8 100644 --- a/device/bluetooth/dbus/fake_bluetooth_media_client.cc +++ b/device/bluetooth/dbus/fake_bluetooth_media_client.cc
@@ -100,8 +100,8 @@ SetEndpointRegistered(endpoints_.begin()->second, false); // Notifies observers about the change on |visible_|. - FOR_EACH_OBSERVER(BluetoothMediaClient::Observer, observers_, - MediaRemoved(object_path_)); + for (auto& observer : observers_) + observer.MediaRemoved(object_path_); } void FakeBluetoothMediaClient::SetEndpointRegistered(
diff --git a/device/bluetooth/dbus/fake_bluetooth_media_transport_client.cc b/device/bluetooth/dbus/fake_bluetooth_media_transport_client.cc index f021ef0d..72f0362 100644 --- a/device/bluetooth/dbus/fake_bluetooth_media_transport_client.cc +++ b/device/bluetooth/dbus/fake_bluetooth_media_transport_client.cc
@@ -193,8 +193,8 @@ ObjectPath transport_path = transport->path; // Notifies observers about the state change of the transport. - FOR_EACH_OBSERVER(BluetoothMediaTransportClient::Observer, observers_, - MediaTransportRemoved(transport_path)); + for (auto& observer : observers_) + observer.MediaTransportRemoved(transport_path); endpoint->ClearConfiguration(transport_path); delete transport; @@ -212,10 +212,10 @@ return; transport->properties->state.ReplaceValue(state); - FOR_EACH_OBSERVER( - BluetoothMediaTransportClient::Observer, observers_, - MediaTransportPropertyChanged( - transport->path, BluetoothMediaTransportClient::kStateProperty)); + for (auto& observer : observers_) { + observer.MediaTransportPropertyChanged( + transport->path, BluetoothMediaTransportClient::kStateProperty); + } } void FakeBluetoothMediaTransportClient::SetVolume( @@ -226,10 +226,10 @@ return; transport->properties->volume.ReplaceValue(volume); - FOR_EACH_OBSERVER( - BluetoothMediaTransportClient::Observer, observers_, - MediaTransportPropertyChanged( - transport->path, BluetoothMediaTransportClient::kVolumeProperty)); + for (auto& observer : observers_) { + observer.MediaTransportPropertyChanged( + transport->path, BluetoothMediaTransportClient::kVolumeProperty); + } } void FakeBluetoothMediaTransportClient::WriteData(
diff --git a/device/bluetooth/test/mock_bluetooth_gatt_notify_session.cc b/device/bluetooth/test/mock_bluetooth_gatt_notify_session.cc index 4123e2d..54726ae 100644 --- a/device/bluetooth/test/mock_bluetooth_gatt_notify_session.cc +++ b/device/bluetooth/test/mock_bluetooth_gatt_notify_session.cc
@@ -40,9 +40,8 @@ MockBluetoothAdapter* adapter, MockBluetoothGattCharacteristic* characteristic, const std::vector<uint8_t>& value) { - FOR_EACH_OBSERVER( - BluetoothAdapter::Observer, adapter->GetObservers(), - GattCharacteristicValueChanged(adapter, characteristic, value)); + for (auto& observer : adapter->GetObservers()) + observer.GattCharacteristicValueChanged(adapter, characteristic, value); } } // namespace device
diff --git a/device/generic_sensor/platform_sensor.cc b/device/generic_sensor/platform_sensor.cc index ced76bb..eb98f34 100644 --- a/device/generic_sensor/platform_sensor.cc +++ b/device/generic_sensor/platform_sensor.cc
@@ -105,7 +105,8 @@ } void PlatformSensor::NotifySensorError() { - FOR_EACH_OBSERVER(Client, clients_, OnSensorError()); + for (auto& observer : clients_) + observer.OnSensorError(); } bool PlatformSensor::UpdateSensorInternal(const ConfigMap& configurations) {
diff --git a/device/hid/hid_service.cc b/device/hid/hid_service.cc index 09b2ab60..1e118ef 100644 --- a/device/hid/hid_service.cc +++ b/device/hid/hid_service.cc
@@ -103,7 +103,8 @@ << device_info->device_id() << "'"; if (enumeration_ready_) { - FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceAdded(device_info)); + for (auto& observer : observer_list_) + observer.OnDeviceAdded(device_info); } } } @@ -116,12 +117,13 @@ scoped_refptr<HidDeviceInfo> device = it->second; if (enumeration_ready_) { - FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceRemoved(device)); + for (auto& observer : observer_list_) + observer.OnDeviceRemoved(device); } devices_.erase(it); if (enumeration_ready_) { - FOR_EACH_OBSERVER(Observer, observer_list_, - OnDeviceRemovedCleanup(device)); + for (auto& observer : observer_list_) + observer.OnDeviceRemovedCleanup(device); } } }
diff --git a/device/hid/input_service_linux.cc b/device/hid/input_service_linux.cc index 75eb352..b9a1ac0 100644 --- a/device/hid/input_service_linux.cc +++ b/device/hid/input_service_linux.cc
@@ -241,12 +241,14 @@ void InputServiceLinux::AddDevice(const InputDeviceInfo& info) { devices_[info.id] = info; - FOR_EACH_OBSERVER(Observer, observers_, OnInputDeviceAdded(info)); + for (auto& observer : observers_) + observer.OnInputDeviceAdded(info); } void InputServiceLinux::RemoveDevice(const std::string& id) { devices_.erase(id); - FOR_EACH_OBSERVER(Observer, observers_, OnInputDeviceRemoved(id)); + for (auto& observer : observers_) + observer.OnInputDeviceRemoved(id); } bool InputServiceLinux::CalledOnValidThread() const {
diff --git a/device/media_transfer_protocol/media_transfer_protocol_manager.cc b/device/media_transfer_protocol/media_transfer_protocol_manager.cc index 5986456..9c07610 100644 --- a/device/media_transfer_protocol/media_transfer_protocol_manager.cc +++ b/device/media_transfer_protocol/media_transfer_protocol_manager.cc
@@ -355,9 +355,8 @@ // Return to avoid giving observers phantom detach events. return; } - FOR_EACH_OBSERVER(Observer, - observers_, - StorageChanged(false /* detach */, storage_name)); + for (auto& observer : observers_) + observer.StorageChanged(false /* detach */, storage_name); } void OnStorageChanged(bool is_attach, const std::string& storage_name) { @@ -398,9 +397,8 @@ // New storage. Add it and let the observers know. storage_info_map_.insert(std::make_pair(storage_name, storage_info)); - FOR_EACH_OBSERVER(Observer, - observers_, - StorageChanged(true /* is attach */, storage_name)); + for (auto& observer : observers_) + observer.StorageChanged(true /* is attach */, storage_name); } void OnGetStorageInfoFromDevice(const MtpStorageInfo& storage_info) {
diff --git a/device/usb/usb_device.cc b/device/usb/usb_device.cc index 06c4f7c14..5e25dcd 100644 --- a/device/usb/usb_device.cc +++ b/device/usb/usb_device.cc
@@ -73,7 +73,8 @@ } void UsbDevice::NotifyDeviceRemoved() { - FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceRemoved(this)); + for (auto& observer : observer_list_) + observer.OnDeviceRemoved(this); } void UsbDevice::OnDisconnect() {
diff --git a/device/usb/usb_service.cc b/device/usb/usb_service.cc index b6e6dca0..1a2cf98 100644 --- a/device/usb/usb_service.cc +++ b/device/usb/usb_service.cc
@@ -53,7 +53,8 @@ UsbService::~UsbService() { for (const auto& map_entry : devices_) map_entry.second->OnDisconnect(); - FOR_EACH_OBSERVER(Observer, observer_list_, WillDestroyUsbService()); + for (auto& observer : observer_list_) + observer.WillDestroyUsbService(); } UsbService::UsbService( @@ -127,15 +128,18 @@ void UsbService::NotifyDeviceAdded(scoped_refptr<UsbDevice> device) { DCHECK(CalledOnValidThread()); - FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceAdded(device)); + for (auto& observer : observer_list_) + observer.OnDeviceAdded(device); } void UsbService::NotifyDeviceRemoved(scoped_refptr<UsbDevice> device) { DCHECK(CalledOnValidThread()); - FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceRemoved(device)); + for (auto& observer : observer_list_) + observer.OnDeviceRemoved(device); device->NotifyDeviceRemoved(); - FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceRemovedCleanup(device)); + for (auto& observer : observer_list_) + observer.OnDeviceRemovedCleanup(device); } } // namespace device
diff --git a/extensions/renderer/extension_helper.cc b/extensions/renderer/extension_helper.cc index f1371fb..461ac3e 100644 --- a/extensions/renderer/extension_helper.cc +++ b/extensions/renderer/extension_helper.cc
@@ -73,8 +73,8 @@ dispatcher_->script_context_set().GetByV8Context(v8_context); if (!script_context) return; - script_context->module_system()->CallModuleMethod("app.window", - "onAppWindowClosed"); + script_context->module_system()->CallModuleMethodSafe("app.window", + "onAppWindowClosed"); } } // namespace extensions
diff --git a/extensions/renderer/json_schema_unittest.cc b/extensions/renderer/json_schema_unittest.cc index b5e878fb..122f0397 100644 --- a/extensions/renderer/json_schema_unittest.cc +++ b/extensions/renderer/json_schema_unittest.cc
@@ -25,7 +25,7 @@ protected: void TestFunction(const std::string& test_name) { - env()->module_system()->CallModuleMethod("json_schema_test", test_name); + env()->module_system()->CallModuleMethodSafe("json_schema_test", test_name); } private:
diff --git a/extensions/renderer/module_system.cc b/extensions/renderer/module_system.cc index b814350e..2fe422a 100644 --- a/extensions/renderer/module_system.cc +++ b/extensions/renderer/module_system.cc
@@ -314,39 +314,14 @@ v8::Local<v8::Context> v8_context = context()->v8_context(); v8::Context::Scope context_scope(v8_context); - v8::Local<v8::String> v8_module_name; - v8::Local<v8::String> v8_method_name; - if (!ToV8String(GetIsolate(), module_name.c_str(), &v8_module_name) || - !ToV8String(GetIsolate(), method_name.c_str(), &v8_method_name)) { - return handle_scope.Escape(v8::Undefined(GetIsolate())); - } + v8::Local<v8::Function> function = + GetModuleFunction(module_name, method_name); - v8::Local<v8::Value> module; - { - NativesEnabledScope natives_enabled(this); - module = RequireForJsInner(v8_module_name); - } - - if (module.IsEmpty() || !module->IsObject()) { - Fatal(context_, - "Failed to get module " + module_name + " to call " + method_name); - return handle_scope.Escape(v8::Undefined(GetIsolate())); - } - - v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(module); - v8::Local<v8::Value> value; - if (!GetProperty(v8_context, object, v8_method_name, &value) || - !value->IsFunction()) { - Fatal(context_, module_name + "." + method_name + " is not a function"); - return handle_scope.Escape(v8::Undefined(GetIsolate())); - } - - v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(value); v8::Local<v8::Value> result; { v8::TryCatch try_catch(GetIsolate()); try_catch.SetCaptureMessage(true); - result = context_->CallFunction(func, argc, argv); + result = context_->CallFunction(function, argc, argv); if (try_catch.HasCaught()) { HandleException(try_catch); result = v8::Undefined(GetIsolate()); @@ -355,6 +330,43 @@ return handle_scope.Escape(result); } +void ModuleSystem::CallModuleMethodSafe(const std::string& module_name, + const std::string& method_name) { + v8::HandleScope handle_scope(GetIsolate()); + v8::Local<v8::Value> no_args; + CallModuleMethodSafe(module_name, method_name, 0, &no_args); +} + +void ModuleSystem::CallModuleMethodSafe( + const std::string& module_name, + const std::string& method_name, + std::vector<v8::Local<v8::Value>>* args) { + CallModuleMethodSafe(module_name, method_name, args->size(), args->data()); +} + +void ModuleSystem::CallModuleMethodSafe(const std::string& module_name, + const std::string& method_name, + int argc, + v8::Local<v8::Value> argv[]) { + TRACE_EVENT2("v8", "v8.callModuleMethodSafe", "module_name", module_name, + "method_name", method_name); + + v8::HandleScope handle_scope(GetIsolate()); + v8::Local<v8::Context> v8_context = context()->v8_context(); + v8::Context::Scope context_scope(v8_context); + + v8::Local<v8::Function> function = + GetModuleFunction(module_name, method_name); + + { + v8::TryCatch try_catch(GetIsolate()); + try_catch.SetCaptureMessage(true); + context_->SafeCallFunction(function, argc, argv); + if (try_catch.HasCaught()) + HandleException(try_catch); + } +} + void ModuleSystem::RegisterNativeHandler( const std::string& name, std::unique_ptr<NativeHandler> native_handler) { @@ -781,4 +793,39 @@ } } +v8::Local<v8::Function> ModuleSystem::GetModuleFunction( + const std::string& module_name, + const std::string& method_name) { + v8::Local<v8::String> v8_module_name; + v8::Local<v8::String> v8_method_name; + v8::Local<v8::Function> function; + if (!ToV8String(GetIsolate(), module_name.c_str(), &v8_module_name) || + !ToV8String(GetIsolate(), method_name.c_str(), &v8_method_name)) { + return function; + } + + v8::Local<v8::Value> module; + { + NativesEnabledScope natives_enabled(this); + module = RequireForJsInner(v8_module_name); + } + + if (module.IsEmpty() || !module->IsObject()) { + Fatal(context_, + "Failed to get module " + module_name + " to call " + method_name); + return function; + } + + v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(module); + v8::Local<v8::Value> value; + if (!GetProperty(context()->v8_context(), object, v8_method_name, &value) || + !value->IsFunction()) { + Fatal(context_, module_name + "." + method_name + " is not a function"); + return function; + } + + function = v8::Local<v8::Function>::Cast(value); + return function; +} + } // namespace extensions
diff --git a/extensions/renderer/module_system.h b/extensions/renderer/module_system.h index 4ce5458..1797f9c 100644 --- a/extensions/renderer/module_system.h +++ b/extensions/renderer/module_system.h
@@ -91,8 +91,11 @@ // Calls the specified method exported by the specified module. This is // equivalent to calling require('module_name').method_name() from JS. - v8::Local<v8::Value> CallModuleMethod(const std::string& module_name, - const std::string& method_name); + // DEPRECATED: see crbug.com/629431 + // TODO(devlin): Remove these. + v8::Local<v8::Value> CallModuleMethod( + const std::string& module_name, + const std::string& method_name); v8::Local<v8::Value> CallModuleMethod( const std::string& module_name, const std::string& method_name, @@ -102,6 +105,17 @@ int argc, v8::Local<v8::Value> argv[]); + // Same as the above, but allows for blocking execution. + void CallModuleMethodSafe(const std::string& module_name, + const std::string& method_name); + void CallModuleMethodSafe(const std::string& module_name, + const std::string& method_name, + std::vector<v8::Local<v8::Value>>* args); + void CallModuleMethodSafe(const std::string& module_name, + const std::string& method_name, + int argc, + v8::Local<v8::Value> argv[]); + // Register |native_handler| as a potential target for requireNative(), so // calls to requireNative(|name|) from JS will return a new object created by // |native_handler|. @@ -218,6 +232,10 @@ // See |clobbered_native_handlers_|. void ClobberExistingNativeHandler(const std::string& name); + // Returns the v8::Function associated with the given module and method name. + v8::Local<v8::Function> GetModuleFunction(const std::string& module_name, + const std::string& method_name); + ScriptContext* context_; // A map from module names to the JS source for that module. GetSource()
diff --git a/extensions/renderer/utils_unittest.cc b/extensions/renderer/utils_unittest.cc index 485c214..b45c359 100644 --- a/extensions/renderer/utils_unittest.cc +++ b/extensions/renderer/utils_unittest.cc
@@ -38,38 +38,39 @@ TEST_F(UtilsUnittest, SuperClass) { ModuleSystem::NativesEnabledScope natives_enabled_scope( env()->module_system()); - env()->module_system()->CallModuleMethod("utils_unittest", "testSuperClass"); + env()->module_system()->CallModuleMethodSafe("utils_unittest", + "testSuperClass"); } TEST_F(UtilsUnittest, PromiseNoResult) { ModuleSystem::NativesEnabledScope natives_enabled_scope( env()->module_system()); - env()->module_system()->CallModuleMethod("utils_unittest", - "testPromiseNoResult"); + env()->module_system()->CallModuleMethodSafe("utils_unittest", + "testPromiseNoResult"); RunResolvedPromises(); } TEST_F(UtilsUnittest, PromiseOneResult) { ModuleSystem::NativesEnabledScope natives_enabled_scope( env()->module_system()); - env()->module_system()->CallModuleMethod("utils_unittest", - "testPromiseOneResult"); + env()->module_system()->CallModuleMethodSafe("utils_unittest", + "testPromiseOneResult"); RunResolvedPromises(); } TEST_F(UtilsUnittest, PromiseTwoResults) { ModuleSystem::NativesEnabledScope natives_enabled_scope( env()->module_system()); - env()->module_system()->CallModuleMethod("utils_unittest", - "testPromiseTwoResults"); + env()->module_system()->CallModuleMethodSafe("utils_unittest", + "testPromiseTwoResults"); RunResolvedPromises(); } TEST_F(UtilsUnittest, PromiseError) { ModuleSystem::NativesEnabledScope natives_enabled_scope( env()->module_system()); - env()->module_system()->CallModuleMethod("utils_unittest", - "testPromiseError"); + env()->module_system()->CallModuleMethodSafe("utils_unittest", + "testPromiseError"); RunResolvedPromises(); }
diff --git a/infra/config/recipes.cfg b/infra/config/recipes.cfg index af5ae02..d8745bc4 100644 --- a/infra/config/recipes.cfg +++ b/infra/config/recipes.cfg
@@ -5,13 +5,13 @@ project_id: "build" url: "https://chromium.googlesource.com/chromium/tools/build.git" branch: "master" - revision: "5993e5340c3d0e30c1c07528f424177cf7c83acc" + revision: "ece187bdc4c389344de942753c06aaa69d0e880e" } deps { project_id: "depot_tools" url: "https://chromium.googlesource.com/chromium/tools/depot_tools.git" branch: "master" - revision: "1c822ade1f731ec35a3d2d3be8805f773df22c4c" + revision: "78de30e1bb2a3065ea78c28305be6782cc7c4e43" } deps { project_id: "recipe_engine"
diff --git a/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Linux_64___ignition_fail.json b/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Linux_64___ignition_fail.json index 06eea344..38b2c57 100644 --- a/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Linux_64___ignition_fail.json +++ b/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Linux_64___ignition_fail.json
@@ -429,6 +429,23 @@ ] }, { + "cmd": [ + "python", + "-u", + "RECIPE_PACKAGE_REPO[build]/scripts/slave/chromium/archive_layout_test_retry_summary.py", + "--retry-summary-json", + "{\"failures\": [], \"ignored\": [\"bad/totally-bad-probably.html\", \"tricky/totally-maybe-not-awesome.html\"]}", + "--build-number", + "571", + "--builder-name", + "V8-Blink Linux 64 - ignition", + "--gs-bucket", + "gs://chromium-layout-test-archives" + ], + "cwd": "[BUILDER_CACHE]/V8_Blink_Linux_64___ignition", + "name": "archive_retry_summary" + }, + { "name": "$result", "recipe_result": null, "status_code": 0
diff --git a/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Linux_64__dbg__fail.json b/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Linux_64__dbg__fail.json index c884de0..aec8b98 100644 --- a/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Linux_64__dbg__fail.json +++ b/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Linux_64__dbg__fail.json
@@ -579,6 +579,23 @@ ] }, { + "cmd": [ + "python", + "-u", + "RECIPE_PACKAGE_REPO[build]/scripts/slave/chromium/archive_layout_test_retry_summary.py", + "--retry-summary-json", + "{\"failures\": [], \"ignored\": [\"bad/totally-bad-probably.html\", \"tricky/totally-maybe-not-awesome.html\"]}", + "--build-number", + "571", + "--builder-name", + "V8-Blink Linux 64 (dbg)", + "--gs-bucket", + "gs://chromium-layout-test-archives" + ], + "cwd": "[BUILDER_CACHE]/V8_Blink_Linux_64__dbg_", + "name": "archive_retry_summary" + }, + { "name": "$result", "recipe_result": null, "status_code": 0
diff --git a/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Linux_64_fail.json b/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Linux_64_fail.json index 40d2886..8fb04676 100644 --- a/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Linux_64_fail.json +++ b/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Linux_64_fail.json
@@ -579,6 +579,23 @@ ] }, { + "cmd": [ + "python", + "-u", + "RECIPE_PACKAGE_REPO[build]/scripts/slave/chromium/archive_layout_test_retry_summary.py", + "--retry-summary-json", + "{\"failures\": [], \"ignored\": [\"bad/totally-bad-probably.html\", \"tricky/totally-maybe-not-awesome.html\"]}", + "--build-number", + "571", + "--builder-name", + "V8-Blink Linux 64", + "--gs-bucket", + "gs://chromium-layout-test-archives" + ], + "cwd": "[BUILDER_CACHE]/V8_Blink_Linux_64", + "name": "archive_retry_summary" + }, + { "name": "$result", "recipe_result": null, "status_code": 0
diff --git a/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Mac_fail.json b/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Mac_fail.json index 84fc62b..b2f7c9b 100644 --- a/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Mac_fail.json +++ b/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Mac_fail.json
@@ -629,6 +629,23 @@ ] }, { + "cmd": [ + "python", + "-u", + "RECIPE_PACKAGE_REPO[build]/scripts/slave/chromium/archive_layout_test_retry_summary.py", + "--retry-summary-json", + "{\"failures\": [], \"ignored\": [\"bad/totally-bad-probably.html\", \"tricky/totally-maybe-not-awesome.html\"]}", + "--build-number", + "571", + "--builder-name", + "V8-Blink Mac", + "--gs-bucket", + "gs://chromium-layout-test-archives" + ], + "cwd": "[BUILDER_CACHE]/V8_Blink_Mac", + "name": "archive_retry_summary" + }, + { "name": "$result", "recipe_result": null, "status_code": 0
diff --git a/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Win_fail.json b/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Win_fail.json index 0d6a3a8..5ebbbb7 100644 --- a/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Win_fail.json +++ b/infra/recipes/blink_downstream.expected/full_client_v8_fyi_V8_Blink_Win_fail.json
@@ -558,6 +558,23 @@ ] }, { + "cmd": [ + "python", + "-u", + "RECIPE_PACKAGE_REPO[build]\\scripts\\slave\\chromium\\archive_layout_test_retry_summary.py", + "--retry-summary-json", + "{\"failures\": [], \"ignored\": [\"bad/totally-bad-probably.html\", \"tricky/totally-maybe-not-awesome.html\"]}", + "--build-number", + "571", + "--builder-name", + "V8-Blink Win", + "--gs-bucket", + "gs://chromium-layout-test-archives" + ], + "cwd": "[BUILDER_CACHE]\\V8_Blink_Win", + "name": "archive_retry_summary" + }, + { "name": "$result", "recipe_result": null, "status_code": 0
diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn index 35a898d..136f5bc 100644 --- a/media/audio/BUILD.gn +++ b/media/audio/BUILD.gn
@@ -60,6 +60,8 @@ "audio_device_name.h", "audio_device_thread.cc", "audio_device_thread.h", + "audio_features.cc", + "audio_features.h", "audio_input_controller.cc", "audio_input_controller.h", "audio_input_device.cc",
diff --git a/media/audio/audio_features.cc b/media/audio/audio_features.cc new file mode 100644 index 0000000..5a5ead4 --- /dev/null +++ b/media/audio/audio_features.cc
@@ -0,0 +1,16 @@ +// 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 "media/audio/audio_features.h" + +namespace features { + +#if defined(OS_CHROMEOS) +// Allows experimentally enables mediaDevices.enumerateDevices() on ChromeOS. +// Default disabled (crbug.com/554168). +const base::Feature kEnumerateAudioDevices{"EnumerateAudioDevices", + base::FEATURE_DISABLED_BY_DEFAULT}; +#endif + +} // namespace features
diff --git a/media/audio/audio_features.h b/media/audio/audio_features.h new file mode 100644 index 0000000..6fc3946 --- /dev/null +++ b/media/audio/audio_features.h
@@ -0,0 +1,20 @@ +// 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 MEDIA_AUDIO_AUDIO_FEATURES_H_ +#define MEDIA_AUDIO_AUDIO_FEATURES_H_ + +#include "base/feature_list.h" +#include "build/build_config.h" +#include "media/base/media_export.h" + +namespace features { + +#if defined(OS_CHROMEOS) +MEDIA_EXPORT extern const base::Feature kEnumerateAudioDevices; +#endif // defined(OS_CHROMEOS) + +} // features + +#endif // MEDIA_AUDIO_AUDIO_FEATURES_H_
diff --git a/media/audio/audio_manager_unittest.cc b/media/audio/audio_manager_unittest.cc index 5cfaf6a..d1b8dd6 100644 --- a/media/audio/audio_manager_unittest.cc +++ b/media/audio/audio_manager_unittest.cc
@@ -364,7 +364,8 @@ }; #if defined(USE_CRAS) -TEST_F(AudioManagerTest, EnumerateInputDevicesCras) { +// TODO(warx): enable the test once crbug.com/554168 is fixed. +TEST_F(AudioManagerTest, DISABLED_EnumerateInputDevicesCras) { // Setup the devices without internal mic, so that it doesn't exist // beamforming capable mic. AudioNodeList audio_nodes; @@ -393,7 +394,8 @@ CheckDeviceNamesCras(device_names, expectation); } -TEST_F(AudioManagerTest, EnumerateOutputDevicesCras) { +// TODO(warx): enable the test once crbug.com/554168 is fixed. +TEST_F(AudioManagerTest, DISABLED_EnumerateOutputDevicesCras) { // Setup the devices without internal mic, so that it doesn't exist // beamforming capable mic. AudioNodeList audio_nodes;
diff --git a/media/audio/cras/audio_manager_cras.cc b/media/audio/cras/audio_manager_cras.cc index a2f0cd3..6df14bc87 100644 --- a/media/audio/cras/audio_manager_cras.cc +++ b/media/audio/cras/audio_manager_cras.cc
@@ -19,6 +19,7 @@ #include "chromeos/audio/audio_device.h" #include "chromeos/audio/cras_audio_handler.h" #include "media/audio/audio_device_description.h" +#include "media/audio/audio_features.h" #include "media/audio/cras/cras_input.h" #include "media/audio/cras/cras_unified.h" #include "media/base/channel_layout.h" @@ -159,12 +160,15 @@ AddBeamformingDevices(device_names); else device_names->push_back(media::AudioDeviceName::CreateDefault()); - chromeos::AudioDeviceList devices; - chromeos::CrasAudioHandler::Get()->GetAudioDevices(&devices); - for (const auto& device : devices) { - if (device.is_input == is_input && device.is_for_simple_usage()) { - device_names->emplace_back(device.display_name, - base::Uint64ToString(device.id)); + + if (base::FeatureList::IsEnabled(features::kEnumerateAudioDevices)) { + chromeos::AudioDeviceList devices; + chromeos::CrasAudioHandler::Get()->GetAudioDevices(&devices); + for (const auto& device : devices) { + if (device.is_input == is_input && device.is_for_simple_usage()) { + device_names->emplace_back(device.display_name, + base::Uint64ToString(device.id)); + } } } }
diff --git a/media/base/android/sdk_media_codec_bridge.cc b/media/base/android/sdk_media_codec_bridge.cc index 3247dd8c..609370b 100644 --- a/media/base/android/sdk_media_codec_bridge.cc +++ b/media/base/android/sdk_media_codec_bridge.cc
@@ -560,13 +560,16 @@ } // static -VideoCodecBridge* VideoCodecBridge::CreateDecoder(const VideoCodec& codec, - bool is_secure, - const gfx::Size& size, - jobject surface, - jobject media_crypto, - bool allow_adaptive_playback, - bool require_software_codec) { +VideoCodecBridge* VideoCodecBridge::CreateDecoder( + const VideoCodec& codec, + bool is_secure, + const gfx::Size& size, + jobject surface, + jobject media_crypto, + const std::vector<uint8_t>& csd0, + const std::vector<uint8_t>& csd1, + bool allow_adaptive_playback, + bool require_software_codec) { if (!MediaCodecUtil::IsMediaCodecAvailable()) return nullptr; @@ -585,6 +588,19 @@ Java_MediaCodecBridge_createVideoDecoderFormat(env, j_mime, size.width(), size.height())); DCHECK(!j_format.is_null()); + + if (!csd0.empty()) { + ScopedJavaLocalRef<jbyteArray> j_csd0 = + base::android::ToJavaByteArray(env, csd0.data(), csd0.size()); + Java_MediaCodecBridge_setCodecSpecificData(env, j_format, 0, j_csd0); + } + + if (!csd1.empty()) { + ScopedJavaLocalRef<jbyteArray> j_csd1 = + base::android::ToJavaByteArray(env, csd1.data(), csd1.size()); + Java_MediaCodecBridge_setCodecSpecificData(env, j_format, 1, j_csd1); + } + if (!Java_MediaCodecBridge_configureVideo(env, bridge->media_codec(), j_format, surface, media_crypto, 0, allow_adaptive_playback)) {
diff --git a/media/base/android/sdk_media_codec_bridge.h b/media/base/android/sdk_media_codec_bridge.h index 9be4cbc..82684af8 100644 --- a/media/base/android/sdk_media_codec_bridge.h +++ b/media/base/android/sdk_media_codec_bridge.h
@@ -136,14 +136,17 @@ // Create, start, and return a VideoCodecBridge decoder or NULL on failure. static VideoCodecBridge* CreateDecoder( - const VideoCodec& codec, // e.g. media::kCodecVP8 - bool is_secure, // Will be used with encrypted content. - const gfx::Size& size, // Output frame size. - jobject surface, // Output surface, optional. - jobject media_crypto, // MediaCrypto object, optional. - bool allow_adaptive_playback = - true, // Should adaptive playback be allowed if supported. - bool require_software_codec = false); // Require software decoder? + const VideoCodec& codec, + bool is_secure, // Will be used with encrypted content. + const gfx::Size& size, // Output frame size. + jobject surface, // Output surface, optional. + jobject media_crypto, // MediaCrypto object, optional. + // Codec specific data. See MediaCodec docs. + const std::vector<uint8_t>& csd0, + const std::vector<uint8_t>& csd1, + // Should adaptive playback be allowed if supported. + bool allow_adaptive_playback = true, + bool require_software_codec = false); // Create, start, and return a VideoCodecBridge encoder or NULL on failure. static VideoCodecBridge* CreateEncoder(
diff --git a/media/base/android/sdk_media_codec_bridge_unittest.cc b/media/base/android/sdk_media_codec_bridge_unittest.cc index f2439d8..c697d74 100644 --- a/media/base/android/sdk_media_codec_bridge_unittest.cc +++ b/media/base/android/sdk_media_codec_bridge_unittest.cc
@@ -153,7 +153,8 @@ std::unique_ptr<media::MediaCodecBridge> media_codec; media_codec.reset(VideoCodecBridge::CreateDecoder( - kCodecH264, false, gfx::Size(640, 480), nullptr, nullptr)); + kCodecH264, false, gfx::Size(640, 480), nullptr, nullptr, + std::vector<uint8_t>(), std::vector<uint8_t>())); } TEST(SdkMediaCodecBridgeTest, DoNormal) { @@ -274,7 +275,8 @@ SKIP_TEST_IF_VP8_DECODER_IS_NOT_SUPPORTED(); std::unique_ptr<VideoCodecBridge> media_codec(VideoCodecBridge::CreateDecoder( - kCodecVP8, false, gfx::Size(320, 240), nullptr, nullptr)); + kCodecVP8, false, gfx::Size(320, 240), nullptr, nullptr, + std::vector<uint8_t>(), std::vector<uint8_t>())); EXPECT_TRUE(media_codec.get()); scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile("vp8-I-frame-320x240"); DecodeMediaFrame(media_codec.get(), buffer->data(), buffer->data_size(), @@ -299,9 +301,10 @@ TEST(SdkMediaCodecBridgeTest, CreateUnsupportedCodec) { EXPECT_EQ(nullptr, AudioCodecBridge::Create(kUnknownAudioCodec)); - EXPECT_EQ(nullptr, VideoCodecBridge::CreateDecoder(kUnknownVideoCodec, false, - gfx::Size(320, 240), - nullptr, nullptr)); + EXPECT_EQ(nullptr, + VideoCodecBridge::CreateDecoder( + kUnknownVideoCodec, false, gfx::Size(320, 240), nullptr, + nullptr, std::vector<uint8_t>(), std::vector<uint8_t>())); } } // namespace media
diff --git a/media/blink/cdm_result_promise_helper.cc b/media/blink/cdm_result_promise_helper.cc index c96121a0..5d0a5e5 100644 --- a/media/blink/cdm_result_promise_helper.cc +++ b/media/blink/cdm_result_promise_helper.cc
@@ -38,16 +38,21 @@ return blink::WebContentDecryptionModuleExceptionNotSupportedError; case MediaKeys::INVALID_STATE_ERROR: return blink::WebContentDecryptionModuleExceptionInvalidStateError; + + // TODO(jrummell): Since InvalidAccess is not returned, thus should be + // renamed to TYPE_ERROR. http://crbug.com/570216#c11. case MediaKeys::INVALID_ACCESS_ERROR: - return blink::WebContentDecryptionModuleExceptionInvalidAccessError; + return blink::WebContentDecryptionModuleExceptionTypeError; case MediaKeys::QUOTA_EXCEEDED_ERROR: return blink::WebContentDecryptionModuleExceptionQuotaExceededError; case MediaKeys::UNKNOWN_ERROR: return blink::WebContentDecryptionModuleExceptionUnknownError; + + // These are deprecated, and should be removed. + // http://crbug.com/570216#c11. case MediaKeys::CLIENT_ERROR: - return blink::WebContentDecryptionModuleExceptionClientError; case MediaKeys::OUTPUT_ERROR: - return blink::WebContentDecryptionModuleExceptionOutputError; + break; } NOTREACHED(); return blink::WebContentDecryptionModuleExceptionUnknownError;
diff --git a/media/blink/webcontentdecryptionmodulesession_impl.cc b/media/blink/webcontentdecryptionmodulesession_impl.cc index 6308ade5..3bef8a9 100644 --- a/media/blink/webcontentdecryptionmodulesession_impl.cc +++ b/media/blink/webcontentdecryptionmodulesession_impl.cc
@@ -256,10 +256,10 @@ DCHECK(thread_checker_.CalledOnValidThread()); // From https://w3c.github.io/encrypted-media/#generateRequest. - // 5. If the Key System implementation represented by this object's cdm + // 6. If the Key System implementation represented by this object's cdm // implementation value does not support initDataType as an Initialization - // Data Type, return a promise rejected with a new DOMException whose name - // is NotSupportedError. String comparison is case-sensitive. + // Data Type, return a promise rejected with a NotSupportedError. + // String comparison is case-sensitive. EmeInitDataType eme_init_data_type = ConvertToEmeInitDataType(init_data_type); if (!IsSupportedKeySystemWithInitDataType(adapter_->GetKeySystem(), eme_init_data_type)) { @@ -271,39 +271,50 @@ return; } - // 9.1 If the init data is not valid for initDataType, reject promise with a - // new DOMException whose name is InvalidAccessError. - // 9.2 Let sanitized init data be a validated and sanitized version of init - // data. The user agent must thoroughly validate the Initialization Data - // before passing it to the CDM. This includes verifying that the length - // and values of fields are reasonable, verifying that values are within - // reasonable limits, and stripping irrelevant, unsupported, or unknown - // data or fields. It is recommended that user agents pre-parse, sanitize, - // and/or generate a fully sanitized version of the Initialization Data. - // If the Initialization Data format specified by initDataType support - // multiple entries, the user agent should remove entries that are not - // needed by the CDM. - // 9.3 If the previous step failed, reject promise with a new DOMException - // whose name is InvalidAccessError. + // 10.1 If the init data is not valid for initDataType, reject promise with + // a newly created TypeError. + // 10.2 Let sanitized init data be a validated and sanitized version of init + // data. The user agent must thoroughly validate the Initialization Data + // before passing it to the CDM. This includes verifying that the length + // and values of fields are reasonable, verifying that values are within + // reasonable limits, and stripping irrelevant, unsupported, or unknown + // data or fields. It is recommended that user agents pre-parse, + // sanitize, and/or generate a fully sanitized version of the + // Initialization Data. If the Initialization Data format specified by + // initDataType supports multiple entries, the user agent should remove + // entries that are not needed by the CDM. The user agent must not + // re-order entries within the Initialization Data. + // 10.3 If the preceding step failed, reject promise with a newly created + // TypeError. std::vector<uint8_t> sanitized_init_data; std::string message; if (!SanitizeInitData(eme_init_data_type, init_data, init_data_length, &sanitized_init_data, &message)) { result.completeWithError( - blink::WebContentDecryptionModuleExceptionInvalidAccessError, 0, + blink::WebContentDecryptionModuleExceptionTypeError, 0, blink::WebString::fromUTF8(message)); return; } - // 9.4 Let session id be the empty string. - // (Done in constructor.) + // 10.4 If sanitized init data is empty, reject promise with a + // NotSupportedError. + if (sanitized_init_data.empty()) { + result.completeWithError( + blink::WebContentDecryptionModuleExceptionNotSupportedError, 0, + "No initialization data provided."); + return; + } - // 9.5 Let message be null. - // (Done by CDM.) + // 10.5 Let session id be the empty string. + // (Done in constructor.) - // 9.6 Let cdm be the CDM instance represented by this object's cdm - // instance value. - // 9.7 Use the cdm to execute the following steps: + // 10.6 Let message be null. + // 10.7 Let message type be null. + // (Done by CDM.) + + // 10.8 Let cdm be the CDM instance represented by this object's cdm + // instance value. + // 10.9 Use the cdm to execute the following steps: adapter_->InitializeNewSession( eme_init_data_type, sanitized_init_data, convertSessionType(session_type), std::unique_ptr<NewSessionCdmPromise>(new NewSessionCdmResultPromise( @@ -320,10 +331,17 @@ DCHECK(session_id_.empty()); DCHECK(thread_checker_.CalledOnValidThread()); + // From https://w3c.github.io/encrypted-media/#load. + // 8.1 Let sanitized session ID be a validated and/or sanitized version of + // sessionId. The user agent should thoroughly validate the sessionId + // value before passing it to the CDM. At a minimum, this should include + // checking that the length and value (e.g. alphanumeric) are reasonable. + // 8.2 If the preceding step failed, or if sanitized session ID is empty, + // reject promise with a newly created TypeError. std::string sanitized_session_id; if (!SanitizeSessionId(session_id, &sanitized_session_id)) { result.completeWithError( - blink::WebContentDecryptionModuleExceptionInvalidAccessError, 0, + blink::WebContentDecryptionModuleExceptionTypeError, 0, "Invalid session ID."); return; } @@ -348,11 +366,21 @@ DCHECK(!session_id_.empty()); DCHECK(thread_checker_.CalledOnValidThread()); + // From https://w3c.github.io/encrypted-media/#update. + // 6.1 Let sanitized response be a validated and/or sanitized version of + // response copy. The user agent should thoroughly validate the response + // before passing it to the CDM. This may include verifying values are + // within reasonable limits, stripping irrelevant data or fields, + // pre-parsing it, sanitizing it, and/or generating a fully sanitized + // version. The user agent should check that the length and values of + // fields are reasonable. Unknown fields should be rejected or removed. + // 6.2 If the preceding step failed, or if sanitized response is empty, + // reject promise with a newly created TypeError. std::vector<uint8_t> sanitized_response; if (!SanitizeResponse(adapter_->GetKeySystem(), response, response_length, &sanitized_response)) { result.completeWithError( - blink::WebContentDecryptionModuleExceptionInvalidAccessError, 0, + blink::WebContentDecryptionModuleExceptionTypeError, 0, "Invalid response."); return; }
diff --git a/media/blink/webencryptedmediaclient_impl.cc b/media/blink/webencryptedmediaclient_impl.cc index 6ad39772..91e6303e 100644 --- a/media/blink/webencryptedmediaclient_impl.cc +++ b/media/blink/webencryptedmediaclient_impl.cc
@@ -138,9 +138,21 @@ GetReporter(request.keySystem())->ReportSupported(); // TODO(sandersd): Pass |are_secure_codecs_required| along and use it to // configure the CDM security level and use of secure surfaces on Android. + + // If the frame is closed while the permission prompt is displayed, + // the permission prompt is dismissed and this may result in the + // requestMediaKeySystemAccess request succeeding. However, the blink + // objects may have been cleared, so check if this is the case and simply + // reject the request. + blink::WebSecurityOrigin origin = request.getSecurityOrigin(); + if (origin.isNull()) { + request.requestNotSupported("Unable to create MediaKeySystemAccess"); + return; + } + request.requestSucceeded(WebContentDecryptionModuleAccessImpl::Create( - request.keySystem(), request.getSecurityOrigin(), - accumulated_configuration, cdm_config, weak_factory_.GetWeakPtr())); + request.keySystem(), origin, accumulated_configuration, cdm_config, + weak_factory_.GetWeakPtr())); } void WebEncryptedMediaClientImpl::OnRequestNotSupported(
diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc index 671c25e9..efe2fe45 100644 --- a/media/filters/gpu_video_decoder.cc +++ b/media/filters/gpu_video_decoder.cc
@@ -5,6 +5,7 @@ #include "media/filters/gpu_video_decoder.h" #include <algorithm> +#include <array> #include <utility> #include "base/bind.h" @@ -31,7 +32,44 @@ #include "media/renderers/gpu_video_accelerator_factories.h" #include "third_party/skia/include/core/SkBitmap.h" +#if defined(USE_PROPRIETARY_CODECS) +#include "media/formats/mp4/box_definitions.h" +#endif + namespace media { +namespace { + +// Size of shared-memory segments we allocate. Since we reuse them we let them +// be on the beefy side. +static const size_t kSharedMemorySegmentBytes = 100 << 10; + +#if defined(OS_ANDROID) && defined(USE_PROPRIETARY_CODECS) +// Extract the SPS and PPS lists from |extra_data|. Each SPS and PPS is prefixed +// with 0x0001, the Annex B framing bytes. The out parameters are not modified +// on failure. +void ExtractSpsAndPps(const std::vector<uint8_t>& extra_data, + std::vector<uint8_t>* sps_out, + std::vector<uint8_t>* pps_out) { + mp4::AVCDecoderConfigurationRecord record; + if (!record.Parse(extra_data.data(), extra_data.size())) { + DVLOG(1) << "Failed to extract the SPS and PPS from extra_data"; + return; + } + + constexpr std::array<uint8_t, 4> prefix = {{0, 0, 0, 1}}; + for (const std::vector<uint8_t>& sps : record.sps_list) { + sps_out->insert(sps_out->end(), prefix.begin(), prefix.end()); + sps_out->insert(sps_out->end(), sps.begin(), sps.end()); + } + + for (const std::vector<uint8_t>& pps : record.pps_list) { + pps_out->insert(pps_out->end(), prefix.begin(), prefix.end()); + pps_out->insert(pps_out->end(), pps.begin(), pps.end()); + } +} +#endif + +} // namespace const char GpuVideoDecoder::kDecoderName[] = "GpuVideoDecoder"; @@ -40,10 +78,6 @@ // resources. enum { kMaxInFlightDecodes = 4 }; -// Size of shared-memory segments we allocate. Since we reuse them we let them -// be on the beefy side. -static const size_t kSharedMemorySegmentBytes = 100 << 10; - GpuVideoDecoder::SHMBuffer::SHMBuffer(std::unique_ptr<base::SharedMemory> m, size_t s) : shm(std::move(m)), size(s) {} @@ -299,6 +333,14 @@ vda_config.surface_id = surface_id; vda_config.is_deferred_initialization_allowed = true; vda_config.initial_expected_coded_size = config_.coded_size(); + +#if defined(OS_ANDROID) && defined(USE_PROPRIETARY_CODECS) + // We pass the SPS and PPS on Android because it lets us initialize + // MediaCodec more reliably (http://crbug.com/649185). + if (config_.codec() == kCodecH264) + ExtractSpsAndPps(config_.extra_data(), &vda_config.sps, &vda_config.pps); +#endif + if (!vda_->Initialize(vda_config, this)) { DVLOG(1) << "VDA::Initialize failed."; base::ResetAndReturn(&init_cb_).Run(false);
diff --git a/media/gpu/android_video_decode_accelerator.cc b/media/gpu/android_video_decode_accelerator.cc index 55eb738..f7851da 100644 --- a/media/gpu/android_video_decode_accelerator.cc +++ b/media/gpu/android_video_decode_accelerator.cc
@@ -380,10 +380,15 @@ codec_config_->codec_ != kCodecHEVC && #endif codec_config_->codec_ != kCodecH264) { - LOG(ERROR) << "Unsupported profile: " << config.profile; + DLOG(ERROR) << "Unsupported profile: " << config.profile; return false; } + if (codec_config_->codec_ == kCodecH264) { + codec_config_->csd0_ = config.sps; + codec_config_->csd1_ = config.pps; + } + // Only use MediaCodec for VP8/9 if it's likely backed by hardware // or if the stream is encrypted. if (IsMediaCodecSoftwareDecodingForbidden() && @@ -397,7 +402,7 @@ auto gles_decoder = get_gles2_decoder_cb_.Run(); if (!gles_decoder) { - LOG(ERROR) << "Failed to get gles2 decoder instance."; + DLOG(ERROR) << "Failed to get gles2 decoder instance."; return false; } @@ -1043,8 +1048,8 @@ std::unique_ptr<VideoCodecBridge> codec(VideoCodecBridge::CreateDecoder( codec_config->codec_, codec_config->needs_protected_surface_, codec_config->initial_expected_coded_size_, - codec_config->surface_.j_surface().obj(), media_crypto, true, - require_software_codec)); + codec_config->surface_.j_surface().obj(), media_crypto, + codec_config->csd0_, codec_config->csd1_, true, require_software_codec)); return codec; }
diff --git a/media/gpu/android_video_decode_accelerator.h b/media/gpu/android_video_decode_accelerator.h index 38b27e9..c8199a2 100644 --- a/media/gpu/android_video_decode_accelerator.h +++ b/media/gpu/android_video_decode_accelerator.h
@@ -126,6 +126,10 @@ // restrict the codec to be software only. AVDACodecAllocator::TaskType task_type_; + // Codec specific data (SPS and PPS for H264). + std::vector<uint8_t> csd0_; + std::vector<uint8_t> csd1_; + protected: friend class base::RefCountedThreadSafe<CodecConfig>; virtual ~CodecConfig();
diff --git a/media/gpu/ipc/common/media_param_traits_macros.h b/media/gpu/ipc/common/media_param_traits_macros.h index 563109c..e650f05 100644 --- a/media/gpu/ipc/common/media_param_traits_macros.h +++ b/media/gpu/ipc/common/media_param_traits_macros.h
@@ -27,6 +27,8 @@ IPC_STRUCT_TRAITS_MEMBER(surface_id) IPC_STRUCT_TRAITS_MEMBER(initial_expected_coded_size) IPC_STRUCT_TRAITS_MEMBER(supported_output_formats) + IPC_STRUCT_TRAITS_MEMBER(sps) + IPC_STRUCT_TRAITS_MEMBER(pps) IPC_STRUCT_TRAITS_END() IPC_STRUCT_TRAITS_BEGIN(media::CreateVideoEncoderParams)
diff --git a/media/gpu/media_foundation_video_encode_accelerator_win.cc b/media/gpu/media_foundation_video_encode_accelerator_win.cc index 6aa3b17..dfd2710 100644 --- a/media/gpu/media_foundation_video_encode_accelerator_win.cc +++ b/media/gpu/media_foundation_video_encode_accelerator_win.cc
@@ -402,7 +402,7 @@ } void MediaFoundationVideoEncodeAccelerator::EncodeTask( - const scoped_refptr<VideoFrame>& frame, + scoped_refptr<VideoFrame> frame, bool force_keyframe) { DVLOG(3) << __func__; DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); @@ -430,6 +430,10 @@ input_sample_->SetSampleTime(frame->timestamp().InMicroseconds() * kOneMicrosecondInMFSampleTimeUnits); input_sample_->SetSampleDuration(kOneSecondInMFSampleTimeUnits / frame_rate_); + + // Release frame after input is copied. + frame = nullptr; + HRESULT hr = encoder_->ProcessInput(0, input_sample_.get(), 0); // According to MSDN, if encoder returns MF_E_NOTACCEPTING, we need to try // processing the output. This error indicates that encoder does not accept
diff --git a/media/gpu/media_foundation_video_encode_accelerator_win.h b/media/gpu/media_foundation_video_encode_accelerator_win.h index 4ad1085..3060abb 100644 --- a/media/gpu/media_foundation_video_encode_accelerator_win.h +++ b/media/gpu/media_foundation_video_encode_accelerator_win.h
@@ -77,7 +77,7 @@ void NotifyError(VideoEncodeAccelerator::Error error); // Encoding tasks to be run on |encoder_thread_|. - void EncodeTask(const scoped_refptr<VideoFrame>& frame, bool force_keyframe); + void EncodeTask(scoped_refptr<VideoFrame> frame, bool force_keyframe); // Checks for and copies encoded output on |encoder_thread_|. void ProcessOutput();
diff --git a/media/video/video_decode_accelerator.h b/media/video/video_decode_accelerator.h index 7e3c940d..b818a055 100644 --- a/media/video/video_decode_accelerator.h +++ b/media/video/video_decode_accelerator.h
@@ -156,6 +156,12 @@ // The list of picture buffer formats that the client knows how to use. An // empty list means any format is supported. std::vector<VideoPixelFormat> supported_output_formats; + + // The H264 SPS and PPS configuration data. Not all clients populate these + // fields, so they should be parsed from the bitstream instead, if required. + // Each SPS and PPS is prefixed with the Annex B framing bytes: 0, 0, 0, 1. + std::vector<uint8_t> sps; + std::vector<uint8_t> pps; }; // Interface for collaborating with picture interface to provide memory for
diff --git a/mojo/public/js/validation_unittests.js b/mojo/public/js/validation_unittests.js index 4bdaa8f..0eb0e4dc 100644 --- a/mojo/public/js/validation_unittests.js +++ b/mojo/public/js/validation_unittests.js
@@ -38,7 +38,7 @@ TestMessageParserFailure.prototype.toString = function() { return 'Error: ' + this.message + ' for "' + this.input + '"'; - } + }; function checkData(data, expectedData, input) { if (data.byteLength != expectedData.byteLength) { @@ -225,8 +225,6 @@ for (var i = 0; i < testFiles.length; i++) { // TODO(hansmuller) Temporarily skipping array pointer overflow tests // because JS numbers are limited to 53 bits. - // TODO(yzshen) Skipping struct versioning tests (tests with "mthd11" - // in the name) because the feature is not supported in JS yet. // TODO(rudominer): Temporarily skipping 'no-such-method', // 'invalid_request_flags', and 'invalid_response_flags' until additional // logic in *RequestValidator and *ResponseValidator is ported from @@ -234,7 +232,6 @@ // TODO(crbug/640298): Implement max recursion depth for JS. // TODO(crbug/628104): Support struct map keys for JS. if (testFiles[i].indexOf("overflow") != -1 || - testFiles[i].indexOf("mthd11") != -1 || testFiles[i].indexOf("conformance_mthd19") != -1 || testFiles[i].indexOf("conformance_mthd20") != -1 || testFiles[i].indexOf("no_such_method") != -1 || @@ -302,7 +299,7 @@ var validationError = noError; testConnection.router_.validationErrorHandler = function(err) { validationError = err; - } + }; testConnection.router_.connector_.waitForNextMessage(); checkValidationResult(testFiles[i], validationError);
diff --git a/mojo/public/js/validator.js b/mojo/public/js/validator.js index 48e36c61..a3f4f00 100644 --- a/mojo/public/js/validator.js +++ b/mojo/public/js/validator.js
@@ -81,7 +81,7 @@ return false; return true; - } + }; Validator.prototype.claimRange = function(start, numBytes) { if (this.isValidRange(start, numBytes)) { @@ -89,7 +89,7 @@ return true; } return false; - } + }; Validator.prototype.claimHandle = function(index) { if (index === codec.kEncodedInvalidHandleValue) @@ -101,7 +101,7 @@ // This is safe because handle indices are uint32. this.handleIndex = index + 1; return true; - } + }; Validator.prototype.validateEnum = function(offset, enumClass, nullable) { // Note: Assumes that enums are always 32 bits! But this matches @@ -120,14 +120,13 @@ if (!this.claimHandle(index)) return validationError.ILLEGAL_HANDLE; return validationError.NONE; - } + }; Validator.prototype.validateInterface = function(offset, nullable) { return this.validateHandle(offset, nullable); - } + }; - Validator.prototype.validateStructHeader = - function(offset, minNumBytes, minVersion) { + Validator.prototype.validateStructHeader = function(offset, minNumBytes) { if (!codec.isAligned(offset)) return validationError.MISALIGNED_OBJECT; @@ -135,20 +134,44 @@ return validationError.ILLEGAL_MEMORY_RANGE; var numBytes = this.message.buffer.getUint32(offset); - var version = this.message.buffer.getUint32(offset + 4); - // Backward compatibility is not yet supported. - if (numBytes < minNumBytes || version < minVersion) + if (numBytes < minNumBytes) return validationError.UNEXPECTED_STRUCT_HEADER; if (!this.claimRange(offset, numBytes)) return validationError.ILLEGAL_MEMORY_RANGE; return validationError.NONE; - } + }; + + Validator.prototype.validateStructVersion = function(offset, versionSizes) { + var numBytes = this.message.buffer.getUint32(offset); + var version = this.message.buffer.getUint32(offset + 4); + + if (version <= versionSizes[versionSizes.length - 1].version) { + // Scan in reverse order to optimize for more recent versionSizes. + for (var i = versionSizes.length - 1; i >= 0; --i) { + if (version >= versionSizes[i].version) { + if (numBytes == versionSizes[i].numBytes) + break; + return validationError.UNEXPECTED_STRUCT_HEADER; + } + } + } else if (numBytes < versionSizes[versionSizes.length-1].numBytes) { + return validationError.UNEXPECTED_STRUCT_HEADER; + } + + return validationError.NONE; + }; + + Validator.prototype.isFieldInStructVersion = function(offset, fieldVersion) { + var structVersion = this.message.buffer.getUint32(offset + 4); + return fieldVersion <= structVersion; + }; Validator.prototype.validateMessageHeader = function() { - var err = this.validateStructHeader(0, codec.kMessageHeaderSize, 0); + + var err = this.validateStructHeader(0, codec.kMessageHeaderSize); if (err != validationError.NONE) return err; @@ -174,7 +197,7 @@ return validationError.MESSAGE_HEADER_INVALID_FLAGS; return validationError.NONE; - } + }; // Returns the message.buffer relative offset this pointer "points to", // NULL_MOJO_POINTER if the pointer represents a null, or JS null if the @@ -185,7 +208,7 @@ return NULL_MOJO_POINTER; var bufferOffset = offset + pointerValue; return Number.isSafeInteger(bufferOffset) ? bufferOffset : null; - } + }; Validator.prototype.decodeUnionSize = function(offset) { return this.message.buffer.getUint32(offset); @@ -208,7 +231,7 @@ return this.validateArray(arrayOffset, elementSize, elementType, expectedDimensionSizes, currentDimension); - } + }; Validator.prototype.validateStructPointer = function( offset, structClass, nullable) { @@ -221,7 +244,7 @@ validationError.NONE : validationError.UNEXPECTED_NULL_POINTER; return structClass.validate(this, structOffset); - } + }; Validator.prototype.validateUnion = function( offset, unionClass, nullable) { @@ -232,7 +255,7 @@ } return unionClass.validate(this, offset); - } + }; Validator.prototype.validateNestedUnion = function( offset, unionClass, nullable) { @@ -245,7 +268,7 @@ validationError.NONE : validationError.UNEXPECTED_NULL_UNION; return this.validateUnion(unionOffset, unionClass, nullable); - } + }; // This method assumes that the array at arrayPointerOffset has // been validated. @@ -253,7 +276,7 @@ Validator.prototype.arrayLength = function(arrayPointerOffset) { var arrayOffset = this.decodePointer(arrayPointerOffset); return this.message.buffer.getUint32(arrayOffset + 4); - } + }; Validator.prototype.validateMapPointer = function( offset, mapIsNullable, keyClass, valueClass, valueIsNullable) { @@ -268,7 +291,7 @@ validationError.NONE : validationError.UNEXPECTED_NULL_POINTER; var mapEncodedSize = codec.kStructHeaderSize + codec.kMapStructPayloadSize; - var err = this.validateStructHeader(structOffset, mapEncodedSize, 0); + var err = this.validateStructHeader(structOffset, mapEncodedSize); if (err !== validationError.NONE) return err; @@ -301,12 +324,12 @@ return validationError.DIFFERENT_SIZED_ARRAYS_IN_MAP; return validationError.NONE; - } + }; Validator.prototype.validateStringPointer = function(offset, nullable) { return this.validateArrayPointer( offset, codec.Uint8.encodedSize, codec.Uint8, nullable, [0], 0); - } + }; // Similar to Array_Data<T>::Validate() // mojo/public/cpp/bindings/lib/array_internal.h @@ -363,7 +386,7 @@ return this.validateEnum(elementsOffset, elementType.cls, nullable); return validationError.NONE; - } + }; // Note: the |offset + i * elementSize| computation in the validateFooElements // methods below is "safe" because elementSize <= 8, offset and @@ -379,7 +402,7 @@ return err; } return validationError.NONE; - } + }; Validator.prototype.validateInterfaceElements = function(offset, numElements, nullable) { @@ -391,7 +414,7 @@ return err; } return validationError.NONE; - } + }; // The elementClass parameter is the element type of the element arrays. Validator.prototype.validateArrayElements = @@ -407,7 +430,7 @@ return err; } return validationError.NONE; - } + }; Validator.prototype.validateStructElements = function(offset, numElements, structClass, nullable) { @@ -420,7 +443,7 @@ return err; } return validationError.NONE; - } + }; var exports = {}; exports.validationError = validationError;
diff --git a/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl index b16d673d..25e532b 100644 --- a/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl +++ b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
@@ -34,16 +34,40 @@ {{struct.name}}.validate = function(messageValidator, offset) { var err; - err = messageValidator.validateStructHeader(offset, {{struct.name}}.encodedSize, {{struct.versions[-1].version}}); + err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize); if (err !== validator.validationError.NONE) return err; + var kVersionSizes = [ +{%- for version in struct.versions %} + {version: {{version.version}}, numBytes: {{version.num_bytes}}}{% if not loop.last %}, + {%- endif -%} +{% endfor %} + ]; + err = messageValidator.validateStructVersion(offset, kVersionSizes); + if (err !== validator.validationError.NONE) + return err; + +{#- Before validating fields introduced at a certain version, we need to add + a version check, which makes sure we skip further validation if |object| + is from an earlier version. |last_checked_version| records the last + version that we have added such version check. #} {%- from "validation_macros.tmpl" import validate_struct_field %} -{%- for packed_field in struct.packed.packed_fields %} +{%- set last_checked_version = 0 %} +{%- for packed_field in struct.packed.packed_fields_in_ordinal_order %} {%- set offset = packed_field|field_offset %} {%- set field = packed_field.field %} {%- set name = struct.name ~ '.' ~ field.name %} +{% if field|is_object_field or field|is_any_handle_or_interface_field or + field|is_enum_field %} +{% if packed_field.min_version > last_checked_version %} +{% set last_checked_version = packed_field.min_version %} + // version check {{name}} + if (!messageValidator.isFieldInStructVersion({{packed_field.min_version}})) + return validator.validationError.NONE; +{%- endif -%} {{validate_struct_field(field, offset, name)|indent(4)}} +{%- endif %} {%- endfor %} return validator.validationError.NONE;
diff --git a/mojo/public/tools/bindings/generators/mojom_js_generator.py b/mojo/public/tools/bindings/generators/mojom_js_generator.py index 7924e8a..3900e50 100644 --- a/mojo/public/tools/bindings/generators/mojom_js_generator.py +++ b/mojo/public/tools/bindings/generators/mojom_js_generator.py
@@ -354,33 +354,44 @@ def IsBoolField(field): return mojom.IsBoolKind(field.kind) +def IsObjectField(field): + return mojom.IsObjectKind(field.kind) + +def IsAnyHandleOrInterfaceField(field): + return mojom.IsAnyHandleOrInterfaceKind(field.kind) + +def IsEnumField(field): + return mojom.IsEnumKind(field.kind) + class Generator(generator.Generator): js_filters = { - "default_value": JavaScriptDefaultValue, - "payload_size": JavaScriptPayloadSize, "decode_snippet": JavaScriptDecodeSnippet, + "default_value": JavaScriptDefaultValue, "encode_snippet": JavaScriptEncodeSnippet, - "union_decode_snippet": JavaScriptUnionDecodeSnippet, - "union_encode_snippet": JavaScriptUnionEncodeSnippet, "expression_to_text": ExpressionToText, "field_offset": JavaScriptFieldOffset, "has_callbacks": mojom.HasCallbacks, + "is_any_handle_or_interface_field": IsAnyHandleOrInterfaceField, "is_array_pointer_field": IsArrayPointerField, "is_bool_field": IsBoolField, "is_enum_field": IsEnumField, - "is_map_pointer_field": IsMapPointerField, - "is_struct_pointer_field": IsStructPointerField, - "is_string_pointer_field": IsStringPointerField, - "is_union_field": IsUnionField, "is_handle_field": IsHandleField, "is_interface_field": IsInterfaceField, "is_interface_request_field": IsInterfaceRequestField, - "js_type": JavaScriptType, + "is_map_pointer_field": IsMapPointerField, + "is_object_field": IsObjectField, + "is_string_pointer_field": IsStringPointerField, + "is_struct_pointer_field": IsStructPointerField, + "is_union_field": IsUnionField, "js_proxy_method_parameter_value": JavaScriptProxyMethodParameterValue, "js_stub_method_parameter_value": JavaScriptStubMethodParameterValue, + "js_type": JavaScriptType, + "payload_size": JavaScriptPayloadSize, "stylize_method": generator.StudlyCapsToCamel, + "union_decode_snippet": JavaScriptUnionDecodeSnippet, + "union_encode_snippet": JavaScriptUnionEncodeSnippet, "validate_array_params": JavaScriptValidateArrayParams, "validate_enum_params": JavaScriptValidateEnumParams, "validate_handle_params": JavaScriptValidateHandleParams,
diff --git a/net/base/file_stream_context.cc b/net/base/file_stream_context.cc index ac94008..f059be7 100644 --- a/net/base/file_stream_context.cc +++ b/net/base/file_stream_context.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "base/debug/alias.h" #include "base/files/file_path.h" #include "base/location.h" #include "base/profiler/scoped_tracker.h" @@ -83,9 +84,7 @@ void FileStream::Context::Open(const base::FilePath& path, int open_flags, const CompletionCallback& callback) { - // TODO(jkarlin): Change back to a DCHECK once https://crbug.com/487732 is - // fixed. - CHECK(!async_in_progress_); + CheckNoAsyncInProgress(); bool posted = base::PostTaskAndReplyWithResult( task_runner_.get(), @@ -95,13 +94,12 @@ base::Bind(&Context::OnOpenCompleted, base::Unretained(this), callback)); DCHECK(posted); + last_operation_ = OPEN; async_in_progress_ = true; } void FileStream::Context::Close(const CompletionCallback& callback) { - // TODO(jkarlin): Change back to a DCHECK once https://crbug.com/487732 is - // fixed. - CHECK(!async_in_progress_); + CheckNoAsyncInProgress(); bool posted = base::PostTaskAndReplyWithResult( task_runner_.get(), FROM_HERE, @@ -111,14 +109,13 @@ IntToInt64(callback))); DCHECK(posted); + last_operation_ = CLOSE; async_in_progress_ = true; } void FileStream::Context::Seek(int64_t offset, const Int64CompletionCallback& callback) { - // TODO(jkarlin): Change back to a DCHECK once https://crbug.com/487732 is - // fixed. - CHECK(!async_in_progress_); + CheckNoAsyncInProgress(); bool posted = base::PostTaskAndReplyWithResult( task_runner_.get(), FROM_HERE, @@ -126,13 +123,12 @@ base::Bind(&Context::OnAsyncCompleted, base::Unretained(this), callback)); DCHECK(posted); + last_operation_ = SEEK; async_in_progress_ = true; } void FileStream::Context::Flush(const CompletionCallback& callback) { - // TODO(jkarlin): Change back to a DCHECK once https://crbug.com/487732 is - // fixed. - CHECK(!async_in_progress_); + CheckNoAsyncInProgress(); bool posted = base::PostTaskAndReplyWithResult( task_runner_.get(), @@ -143,6 +139,7 @@ IntToInt64(callback))); DCHECK(posted); + last_operation_ = FLUSH; async_in_progress_ = true; } @@ -150,6 +147,16 @@ return file_.IsValid(); } +void FileStream::Context::CheckNoAsyncInProgress() const { + if (!async_in_progress_) + return; + LastOperation state = last_operation_; + base::debug::Alias(&state); + // TODO(xunjieli): Once https://crbug.com/487732 is fixed, use + // DCHECK(!async_in_progress_) directly at call places. + CHECK(!async_in_progress_); +} + FileStream::Context::OpenResult FileStream::Context::OpenFileImpl( const base::FilePath& path, int open_flags) { #if defined(OS_POSIX) @@ -208,13 +215,12 @@ // TODO(ananta) // Replace this CHECK with a DCHECK once we figure out the root cause of // http://crbug.com/455066 - CHECK(!async_in_progress_); + CheckNoAsyncInProgress(); if (file_.IsValid()) { bool posted = task_runner_.get()->PostTask( - FROM_HERE, - base::Bind(base::IgnoreResult(&Context::CloseFileImpl), - base::Owned(this))); + FROM_HERE, base::Bind(base::IgnoreResult(&Context::CloseFileImpl), + base::Owned(this))); DCHECK(posted); } else { delete this; @@ -237,6 +243,7 @@ // should be reset before Close() because it shouldn't run if any async // operation is in progress. async_in_progress_ = false; + last_operation_ = NONE; if (orphaned_) CloseAndDelete(); else
diff --git a/net/base/file_stream_context.h b/net/base/file_stream_context.h index aaff5adb..6b44580 100644 --- a/net/base/file_stream_context.h +++ b/net/base/file_stream_context.h
@@ -125,6 +125,27 @@ DISALLOW_COPY_AND_ASSIGN(OpenResult); }; + // TODO(xunjieli): Remove after crbug.com/487732 is fixed. + enum LastOperation { + // FileStream has a pending Open(). + OPEN, + // FileStream has a pending Write(). + WRITE, + // FileStream has a pending Read(). + READ, + // FileStream has a pending Seek(). + SEEK, + // FileStream has a pending Flush(). + FLUSH, + // FileStream has a pending Close(). + CLOSE, + // FileStream doesn't have any pending operation. + NONE + }; + + // TODO(xunjieli): Remove after crbug.com/487732 is fixed. + void CheckNoAsyncInProgress() const; + //////////////////////////////////////////////////////////////////////////// // Platform-independent methods implemented in file_stream_context.cc. //////////////////////////////////////////////////////////////////////////// @@ -217,6 +238,10 @@ base::File file_; bool async_in_progress_; + + // TODO(xunjieli): Remove after crbug.com/487732 is fixed. + LastOperation last_operation_; + bool orphaned_; scoped_refptr<base::TaskRunner> task_runner_;
diff --git a/net/base/file_stream_context_posix.cc b/net/base/file_stream_context_posix.cc index af98c782..67c772a0 100644 --- a/net/base/file_stream_context_posix.cc +++ b/net/base/file_stream_context_posix.cc
@@ -32,6 +32,7 @@ const scoped_refptr<base::TaskRunner>& task_runner) : file_(std::move(file)), async_in_progress_(false), + last_operation_(NONE), orphaned_(false), task_runner_(task_runner) {} @@ -41,7 +42,7 @@ int FileStream::Context::Read(IOBuffer* in_buf, int buf_len, const CompletionCallback& callback) { - DCHECK(!async_in_progress_); + CheckNoAsyncInProgress(); scoped_refptr<IOBuffer> buf = in_buf; const bool posted = base::PostTaskAndReplyWithResult( @@ -54,13 +55,14 @@ DCHECK(posted); async_in_progress_ = true; + last_operation_ = READ; return ERR_IO_PENDING; } int FileStream::Context::Write(IOBuffer* in_buf, int buf_len, const CompletionCallback& callback) { - DCHECK(!async_in_progress_); + CheckNoAsyncInProgress(); scoped_refptr<IOBuffer> buf = in_buf; const bool posted = base::PostTaskAndReplyWithResult( @@ -73,6 +75,7 @@ DCHECK(posted); async_in_progress_ = true; + last_operation_ = WRITE; return ERR_IO_PENDING; }
diff --git a/net/base/file_stream_context_win.cc b/net/base/file_stream_context_win.cc index 5028f1959..4e417dc 100644 --- a/net/base/file_stream_context_win.cc +++ b/net/base/file_stream_context_win.cc
@@ -38,6 +38,7 @@ FileStream::Context::Context(const scoped_refptr<base::TaskRunner>& task_runner) : async_in_progress_(false), + last_operation_(NONE), orphaned_(false), task_runner_(task_runner), async_read_initiated_(false), @@ -49,6 +50,7 @@ const scoped_refptr<base::TaskRunner>& task_runner) : file_(std::move(file)), async_in_progress_(false), + last_operation_(NONE), orphaned_(false), task_runner_(task_runner), async_read_initiated_(false), @@ -67,11 +69,13 @@ int FileStream::Context::Read(IOBuffer* buf, int buf_len, const CompletionCallback& callback) { - CHECK(!async_in_progress_); + CheckNoAsyncInProgress(); + DCHECK(!async_read_initiated_); DCHECK(!async_read_completed_); DCHECK(!io_complete_for_read_received_); + last_operation_ = READ; IOCompletionIsPending(callback, buf); async_read_initiated_ = true; @@ -88,8 +92,9 @@ int FileStream::Context::Write(IOBuffer* buf, int buf_len, const CompletionCallback& callback) { - CHECK(!async_in_progress_); + CheckNoAsyncInProgress(); + last_operation_ = WRITE; result_ = 0; DWORD bytes_written = 0; @@ -137,8 +142,10 @@ DCHECK(!callback_.is_null()); DCHECK(async_in_progress_); - if (!async_read_initiated_) + if (!async_read_initiated_) { + last_operation_ = NONE; async_in_progress_ = false; + } if (orphaned_) { io_complete_for_read_received_ = true; @@ -178,6 +185,7 @@ async_read_initiated_ = false; io_complete_for_read_received_ = false; async_read_completed_ = false; + last_operation_ = NONE; async_in_progress_ = false; } CompletionCallback temp_callback = callback_; @@ -188,6 +196,7 @@ } void FileStream::Context::DeleteOrphanedContext() { + last_operation_ = NONE; async_in_progress_ = false; callback_.Reset(); in_flight_buf_ = NULL;
diff --git a/net/http/http_request_headers.cc b/net/http/http_request_headers.cc index 8e667fc4..70f6d27a 100644 --- a/net/http/http_request_headers.cc +++ b/net/http/http_request_headers.cc
@@ -11,6 +11,7 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/values.h" +#include "net/base/escape.h" #include "net/http/http_log_util.h" #include "net/http/http_util.h" #include "net/log/net_log_capture_mode.h" @@ -188,14 +189,16 @@ const std::string* request_line, NetLogCaptureMode capture_mode) const { std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); - dict->SetString("line", *request_line); + dict->SetString("line", EscapeNonASCII(*request_line)); base::ListValue* headers = new base::ListValue(); - for (HeaderVector::const_iterator it = headers_.begin(); - it != headers_.end(); ++it) { + for (HeaderVector::const_iterator it = headers_.begin(); it != headers_.end(); + ++it) { std::string log_value = ElideHeaderValueForNetLog(capture_mode, it->key, it->value); - headers->AppendString( - base::StringPrintf("%s: %s", it->key.c_str(), log_value.c_str())); + std::string escaped_name = EscapeNonASCII(it->key); + std::string escaped_value = EscapeNonASCII(log_value); + headers->AppendString(base::StringPrintf("%s: %s", escaped_name.c_str(), + escaped_value.c_str())); } dict->Set("headers", headers); return std::move(dict);
diff --git a/net/net.gypi b/net/net.gypi index 3b28267..f4a5fdfc 100644 --- a/net/net.gypi +++ b/net/net.gypi
@@ -212,6 +212,8 @@ 'socket/connection_attempts.h', 'socket/next_proto.h', 'socket/socket.h', + 'socket/socket_bio_adapter.cc', + 'socket/socket_bio_adapter.h', 'socket/socket_performance_watcher.h', 'socket/socket_performance_watcher_factory.h', 'socket/ssl_client_socket.cc', @@ -1831,6 +1833,7 @@ 'socket/mock_client_socket_pool_manager.cc', 'socket/mock_client_socket_pool_manager.h', 'socket/sequenced_socket_data_unittest.cc', + 'socket/socket_bio_adapter_unittest.cc', 'socket/socks5_client_socket_unittest.cc', 'socket/socks_client_socket_pool_unittest.cc', 'socket/socks_client_socket_unittest.cc',
diff --git a/net/nqe/observation_buffer.h b/net/nqe/observation_buffer.h index 9a4ce3a..b9339a8 100644 --- a/net/nqe/observation_buffer.h +++ b/net/nqe/observation_buffer.h
@@ -70,20 +70,18 @@ // Returns true iff the |percentile| value of the observations in this // buffer is available. Sets |result| to the computed |percentile| - // value among all observations since |begin_timestamp|. If the value is - // unavailable, false is returned and |result| is not modified. Percentile - // value is unavailable if all the values in observation buffer are older - // than |begin_timestamp|. + // value of all observations made on or after |begin_timestamp|. If the + // value is unavailable, false is returned and |result| is not modified. + // Percentile value is unavailable if all the values in observation buffer are + // older than |begin_timestamp|. // |result| must not be null. // TODO(tbansal): Move out param |result| as the last param of the function. - bool GetPercentile(const base::TimeTicks& begin_timestamp, + bool GetPercentile(base::TimeTicks begin_timestamp, ValueType* result, int percentile, const std::vector<NetworkQualityObservationSource>& disallowed_observation_sources) const { - DCHECK(result); - DCHECK_GE(Capacity(), Size()); - // Stores WeightedObservation in increasing order of value. + // Stores weighted observations in increasing order by value. std::vector<WeightedObservation<ValueType>> weighted_observations; // Total weight of all observations in |weighted_observations|. @@ -94,13 +92,6 @@ if (weighted_observations.empty()) return false; - DCHECK(!weighted_observations.empty()); - DCHECK_GT(total_weight, 0.0); - - // |weighted_observations| may have a smaller size than observations_ since - // the former contains only the observations later than begin_timestamp. - DCHECK_GE(observations_.size(), weighted_observations.size()); - double desired_weight = percentile / 100.0 * total_weight; double cumulative_weight_seen_so_far = 0.0; @@ -122,6 +113,72 @@ return true; } + // Returns true iff the weighted average of the observations in this + // buffer is available. Sets |result| to the computed weighted average value + // of all observations made on or after |begin_timestamp|. If the value is + // unavailable, false is returned and |result| is not modified. The unweighted + // average value is unavailable if all the values in the observation buffer + // are older than |begin_timestamp|. |result| must not be null. + bool GetWeightedAverage(base::TimeTicks begin_timestamp, + const std::vector<NetworkQualityObservationSource>& + disallowed_observation_sources, + ValueType* result) const { + // Stores weighted observations in increasing order by value. + std::vector<WeightedObservation<ValueType>> weighted_observations; + + // Total weight of all observations in |weighted_observations|. + double total_weight = 0.0; + + ComputeWeightedObservations(begin_timestamp, weighted_observations, + &total_weight, disallowed_observation_sources); + if (weighted_observations.empty()) + return false; + + // Weighted average is the sum of observations times their respective + // weights, divided by the sum of the weights of all observations. + double total_weight_times_value = 0.0; + for (const auto& weighted_observation : weighted_observations) { + total_weight_times_value += + (weighted_observation.weight * + ConvertValueTypeToDouble(weighted_observation.value)); + } + + ConvertDoubleToValueType(total_weight_times_value / total_weight, result); + return true; + } + + // Returns true iff the unweighted average of the observations in this buffer + // is available. Sets |result| to the computed unweighted average value of + // all observations made on or after |begin_timestamp|. If the value is + // unavailable, false is returned and |result| is not modified. The weighted + // average value is unavailable if all the values in the observation buffer + // are older than |begin_timestamp|. |result| must not be null. + bool GetUnweightedAverage(base::TimeTicks begin_timestamp, + const std::vector<NetworkQualityObservationSource>& + disallowed_observation_sources, + ValueType* result) const { + // Stores weighted observations in increasing order by value. + std::vector<WeightedObservation<ValueType>> weighted_observations; + + // Total weight of all observations in |weighted_observations|. + double total_weight = 0.0; + + ComputeWeightedObservations(begin_timestamp, weighted_observations, + &total_weight, disallowed_observation_sources); + if (weighted_observations.empty()) + return false; + + // The unweighted average is the sum of all observations divided by the + // number of observations. + double total_value = 0.0; + for (const auto& weighted_observation : weighted_observations) + total_value += ConvertValueTypeToDouble(weighted_observation.value); + + ConvertDoubleToValueType(total_value / weighted_observations.size(), + result); + return true; + } + void SetTickClockForTesting(std::unique_ptr<base::TickClock> tick_clock) { tick_clock_ = std::move(tick_clock); } @@ -130,6 +187,21 @@ // Maximum number of observations that can be held in the ObservationBuffer. static const size_t kMaximumObservationsBufferSize = 300; + // Convert different ValueTypes to double to make it possible to perform + // arithmetic operations on them. + double ConvertValueTypeToDouble(base::TimeDelta input) const { + return input.InMilliseconds(); + } + double ConvertValueTypeToDouble(int32_t input) const { return input; } + + // Convert double to different ValueTypes. + void ConvertDoubleToValueType(double input, base::TimeDelta* output) const { + *output = base::TimeDelta::FromMilliseconds(input); + } + void ConvertDoubleToValueType(double input, int32_t* output) const { + *output = input; + } + // Computes the weighted observations and stores them in // |weighted_observations| sorted by ascending |WeightedObservation.value|. // Only the observations with timestamp later than |begin_timestamp| are @@ -171,6 +243,14 @@ // Sort the samples by value in ascending order. std::sort(weighted_observations.begin(), weighted_observations.end()); *total_weight = total_weight_observations; + + DCHECK_LE(0.0, *total_weight); + DCHECK(weighted_observations.empty() || 0.0 < *total_weight); + + // |weighted_observations| may have a smaller size than |observations_| + // since the former contains only the observations later than + // |begin_timestamp|. + DCHECK_GE(observations_.size(), weighted_observations.size()); } // Holds observations sorted by time, with the oldest observation at the
diff --git a/net/nqe/observation_buffer_unittest.cc b/net/nqe/observation_buffer_unittest.cc index 4da54e44..501c074 100644 --- a/net/nqe/observation_buffer_unittest.cc +++ b/net/nqe/observation_buffer_unittest.cc
@@ -185,7 +185,7 @@ ObservationBuffer<int32_t> int_buffer(0.5); ObservationBuffer<base::TimeDelta> time_delta_buffer(0.5); const base::TimeTicks now = base::TimeTicks::Now(); - const base::TimeTicks very_old = now - base::TimeDelta::FromDays(365); + const base::TimeTicks very_old = now - base::TimeDelta::FromDays(7); int32_t result; base::TimeDelta time_delta_result; @@ -198,7 +198,7 @@ base::TimeTicks(), &time_delta_result, 50, std::vector<NetworkQualityObservationSource>())); - // First 50 samples have very old timestamp. + // First 50 samples have very old timestamps. for (int i = 1; i <= 50; ++i) { int_buffer.AddObservation(Observation<int32_t>( i, very_old, NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST)); @@ -207,7 +207,7 @@ NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST)); } - // Next 50 (i.e., from 51 to 100) have recent timestamp. + // Next 50 (i.e., from 51 to 100) have recent timestamps. for (int i = 51; i <= 100; ++i) { int_buffer.AddObservation(Observation<int32_t>( i, now, NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST)); @@ -224,12 +224,11 @@ // what is expected due to floating point computation errors and integer // rounding off errors. EXPECT_TRUE(int_buffer.GetPercentile( - base::TimeTicks(), &result, i, - std::vector<NetworkQualityObservationSource>())); + very_old, &result, i, std::vector<NetworkQualityObservationSource>())); EXPECT_NEAR(result, 51 + 0.49 * i, 1); EXPECT_TRUE(time_delta_buffer.GetPercentile( - base::TimeTicks(), &time_delta_result, i, + very_old, &time_delta_result, i, std::vector<NetworkQualityObservationSource>())); EXPECT_NEAR(time_delta_result.InMilliseconds(), 51 + 0.49 * i, 1); } @@ -242,6 +241,122 @@ std::vector<NetworkQualityObservationSource>())); } +#if !defined(OS_WIN) +// Disabled on OS_WIN since the GetUnweightedAverage() and +// GetUnweightedAverage() functions are not yet called outside tests, and so the +// compiler on Windows does not generate the object code for these functions. +// TODO(tbansal): crbug.com/656170 Enable these tests on Windows once these +// functions are called outside the tests. +TEST(NetworkQualityObservationBufferTest, + UnweightedAverageDifferentTimestamps) { + ObservationBuffer<base::TimeDelta> time_delta_buffer(0.5); + ObservationBuffer<int32_t> int_buffer(0.5); + const base::TimeTicks now = base::TimeTicks::Now(); + const base::TimeTicks very_old = now - base::TimeDelta::FromDays(7); + + base::TimeDelta time_delta_result; + int32_t int_result; + + // Network quality should be unavailable when no observations are available. + EXPECT_FALSE(time_delta_buffer.GetUnweightedAverage( + base::TimeTicks(), std::vector<NetworkQualityObservationSource>(), + &time_delta_result)); + EXPECT_FALSE(int_buffer.GetUnweightedAverage( + base::TimeTicks(), std::vector<NetworkQualityObservationSource>(), + &int_result)); + + // The first 50 samples have very old timestamps. + for (int i = 1; i <= 50; ++i) { + time_delta_buffer.AddObservation(Observation<base::TimeDelta>( + base::TimeDelta::FromMilliseconds(i), very_old, + NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST)); + int_buffer.AddObservation(Observation<int32_t>( + i, very_old, NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST)); + } + + // The next 50 (i.e., from 51 to 100) samples have recent timestamps. + for (int i = 51; i <= 100; ++i) { + time_delta_buffer.AddObservation(Observation<base::TimeDelta>( + base::TimeDelta::FromMilliseconds(i), now, + NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST)); + int_buffer.AddObservation(Observation<int32_t>( + i, now, NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST)); + } + + // All samples have equal weight. So, the unweighted average is the average of + // all samples. + EXPECT_TRUE(time_delta_buffer.GetUnweightedAverage( + very_old, std::vector<NetworkQualityObservationSource>(), + &time_delta_result)); + EXPECT_NEAR(time_delta_result.InMilliseconds(), (1 + 100) / 2, 1); + + EXPECT_TRUE(int_buffer.GetUnweightedAverage( + very_old, std::vector<NetworkQualityObservationSource>(), &int_result)); + EXPECT_NEAR(int_result, (1 + 100) / 2, 1); + + EXPECT_FALSE(time_delta_buffer.GetUnweightedAverage( + now + base::TimeDelta::FromSeconds(1), + std::vector<NetworkQualityObservationSource>(), &time_delta_result)); + EXPECT_FALSE(int_buffer.GetUnweightedAverage( + now + base::TimeDelta::FromSeconds(1), + std::vector<NetworkQualityObservationSource>(), &int_result)); +} + +TEST(NetworkQualityObservationBufferTest, WeightedAverageDifferentTimestamps) { + ObservationBuffer<base::TimeDelta> time_delta_buffer(0.5); + ObservationBuffer<int32_t> int_buffer(0.5); + const base::TimeTicks now = base::TimeTicks::Now(); + const base::TimeTicks very_old = now - base::TimeDelta::FromDays(7); + + base::TimeDelta time_delta_result; + int32_t int_result; + + // Network quality should be unavailable when no observations are available. + EXPECT_FALSE(time_delta_buffer.GetWeightedAverage( + base::TimeTicks(), std::vector<NetworkQualityObservationSource>(), + &time_delta_result)); + EXPECT_FALSE(int_buffer.GetWeightedAverage( + base::TimeTicks(), std::vector<NetworkQualityObservationSource>(), + &int_result)); + + // The first 50 samples have very old timestamps. + for (int i = 1; i <= 50; ++i) { + time_delta_buffer.AddObservation(Observation<base::TimeDelta>( + base::TimeDelta::FromMilliseconds(i), very_old, + NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST)); + int_buffer.AddObservation(Observation<int32_t>( + i, very_old, NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST)); + } + + // The next 50 (i.e., from 51 to 100) samples have recent timestamps. + for (int i = 51; i <= 100; ++i) { + time_delta_buffer.AddObservation(Observation<base::TimeDelta>( + base::TimeDelta::FromMilliseconds(i), now, + NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST)); + int_buffer.AddObservation(Observation<int32_t>( + i, now, NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST)); + } + + // The older samples have very little weight, and so the weighted average must + // be approximately equal to the average of all recent samples. + EXPECT_TRUE(time_delta_buffer.GetWeightedAverage( + very_old, std::vector<NetworkQualityObservationSource>(), + &time_delta_result)); + EXPECT_NEAR(time_delta_result.InMilliseconds(), (51 + 100) / 2, 1); + + EXPECT_TRUE(int_buffer.GetWeightedAverage( + very_old, std::vector<NetworkQualityObservationSource>(), &int_result)); + EXPECT_NEAR(int_result, (51 + 100) / 2, 1); + + EXPECT_FALSE(time_delta_buffer.GetWeightedAverage( + now + base::TimeDelta::FromSeconds(1), + std::vector<NetworkQualityObservationSource>(), &time_delta_result)); + EXPECT_FALSE(int_buffer.GetWeightedAverage( + now + base::TimeDelta::FromSeconds(1), + std::vector<NetworkQualityObservationSource>(), &int_result)); +} +#endif // !defined(OS_WIN) + // Verifies that the percentiles are correctly computed when some of the // observation sources are disallowed. All observations have the same timestamp. TEST(NetworkQualityObservationBufferTest, DisallowedObservationSources) {
diff --git a/net/quic/core/crypto/crypto_handshake_message.cc b/net/quic/core/crypto/crypto_handshake_message.cc index 9fcf6ff0..530d2f80 100644 --- a/net/quic/core/crypto/crypto_handshake_message.cc +++ b/net/quic/core/crypto/crypto_handshake_message.cc
@@ -227,6 +227,9 @@ case kMSPC: case kSRBF: case kSWND: + case kMIDS: + case kSCLS: + case kTCID: // uint32_t value if (it->second.size() == 4) { uint32_t value;
diff --git a/net/quic/core/quic_flags_list.h b/net/quic/core/quic_flags_list.h index 15e1b7a..ba7f31c 100644 --- a/net/quic/core/quic_flags_list.h +++ b/net/quic/core/quic_flags_list.h
@@ -162,3 +162,7 @@ // If true, enable bugfix for FHOL experiment (fin-only // WritevStreamData). QUIC_FLAG(bool, FLAGS_quic_bugfix_fhol_writev_fin_only_v2, true) + +// If true, v33 QUIC client uses 1 bit to specify 8-byte connection id in +// public flag. +QUIC_FLAG(bool, FLAGS_quic_remove_v33_hacks2, false)
diff --git a/net/quic/core/quic_framer.cc b/net/quic/core/quic_framer.cc index 6111e8d1..c94749f9 100644 --- a/net/quic/core/quic_framer.cc +++ b/net/quic/core/quic_framer.cc
@@ -724,6 +724,11 @@ case PACKET_8BYTE_CONNECTION_ID: if (quic_version_ > QUIC_VERSION_32) { public_flags |= PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID; + if (!FLAGS_quic_remove_v33_hacks2 && + perspective_ == Perspective::IS_CLIENT) { + public_flags |= PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID_OLD; + } + } else { public_flags |= PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID_OLD; }
diff --git a/net/quic/core/quic_framer_test.cc b/net/quic/core/quic_framer_test.cc index cd43369..1ae77ee 100644 --- a/net/quic/core/quic_framer_test.cc +++ b/net/quic/core/quic_framer_test.cc
@@ -4232,7 +4232,8 @@ unsigned char packet[] = { // public flags (version, 8 byte connection_id) static_cast<unsigned char>( - framer_.version() > QUIC_VERSION_32 ? 0x39 : 0x3D), + (FLAGS_quic_remove_v33_hacks2 && + framer_.version() > QUIC_VERSION_32) ? 0x39 : 0x3D), // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag @@ -4253,7 +4254,8 @@ }; unsigned char packet_34[] = { // public flags (version, 8 byte connection_id) - 0x39, + static_cast<unsigned char>( + FLAGS_quic_remove_v33_hacks2 ? 0x39 : 0x3D), // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag @@ -4383,7 +4385,8 @@ unsigned char packet[] = { // public flags (8 byte connection_id) static_cast<unsigned char>( - framer_.version() > QUIC_VERSION_32 ? 0x79 : 0x7D), + (FLAGS_quic_remove_v33_hacks2 && + framer_.version() > QUIC_VERSION_32) ? 0x79 : 0x7D), // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, @@ -4411,7 +4414,8 @@ }; unsigned char packet_34[] = { // public flags (8 byte connection_id) - 0x79, + static_cast<unsigned char>( + FLAGS_quic_remove_v33_hacks2 ? 0x79 : 0x7D), // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
diff --git a/net/socket/socket_bio_adapter.cc b/net/socket/socket_bio_adapter.cc new file mode 100644 index 0000000..7f769c1 --- /dev/null +++ b/net/socket/socket_bio_adapter.cc
@@ -0,0 +1,340 @@ +// 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 "net/socket/socket_bio_adapter.h" + +#include <openssl/bio.h> +#include <string.h> + +#include <algorithm> + +#include "base/bind.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/threading/thread_task_runner_handle.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/socket/stream_socket.h" +#include "net/ssl/openssl_ssl_util.h" + +namespace net { + +SocketBIOAdapter::SocketBIOAdapter(StreamSocket* socket, + int read_buffer_capacity, + int write_buffer_capacity, + Delegate* delegate) + : socket_(socket), + read_buffer_capacity_(read_buffer_capacity), + read_offset_(0), + read_result_(0), + write_buffer_capacity_(write_buffer_capacity), + write_buffer_used_(0), + write_error_(OK), + delegate_(delegate), + weak_factory_(this) { + bio_.reset(BIO_new(&kBIOMethod)); + bio_->ptr = this; + bio_->init = 1; + + read_callback_ = base::Bind(&SocketBIOAdapter::OnSocketReadComplete, + weak_factory_.GetWeakPtr()); + write_callback_ = base::Bind(&SocketBIOAdapter::OnSocketWriteComplete, + weak_factory_.GetWeakPtr()); +} + +SocketBIOAdapter::~SocketBIOAdapter() { + // BIOs are reference-counted and may outlive the adapter. Clear the pointer + // so future operations fail. + bio_->ptr = nullptr; +} + +bool SocketBIOAdapter::HasPendingReadData() { + return read_result_ > 0; +} + +int SocketBIOAdapter::BIORead(char* out, int len) { + if (len <= 0) + return len; + + // If there is no result available synchronously, report any Write() errors + // that were observed. Otherwise the application may have encountered a socket + // error while writing that would otherwise not be reported until the + // application attempted to write again - which it may never do. See + // https://crbug.com/249848. + if (write_error_ != OK && write_error_ != ERR_IO_PENDING && + (read_result_ == 0 || read_result_ == ERR_IO_PENDING)) { + OpenSSLPutNetError(FROM_HERE, write_error_); + return -1; + } + + if (read_result_ == 0) { + // Instantiate the read buffer and read from the socket. Although only |len| + // bytes were requested, intentionally read to the full buffer size. The SSL + // layer reads the record header and body in separate reads to avoid + // overreading, but issuing one is more efficient. SSL sockets are not + // reused after shutdown for non-SSL traffic, so overreading is fine. + DCHECK(!read_buffer_); + DCHECK_EQ(0, read_offset_); + read_buffer_ = new IOBuffer(read_buffer_capacity_); + int result = socket_->Read(read_buffer_.get(), read_buffer_capacity_, + read_callback_); + if (result == ERR_IO_PENDING) { + read_result_ = ERR_IO_PENDING; + } else { + HandleSocketReadResult(result); + } + } + + // There is a pending Read(). Inform the caller to retry when it completes. + if (read_result_ == ERR_IO_PENDING) { + BIO_set_retry_read(bio()); + return -1; + } + + // If the last Read() failed, report the error. + if (read_result_ < 0) { + OpenSSLPutNetError(FROM_HERE, read_result_); + return -1; + } + + // Report the result of the last Read() if non-empty. + CHECK_LT(read_offset_, read_result_); + len = std::min(len, read_result_ - read_offset_); + memcpy(out, read_buffer_->data() + read_offset_, len); + read_offset_ += len; + + // Release the buffer when empty. + if (read_offset_ == read_result_) { + read_buffer_ = nullptr; + read_offset_ = 0; + read_result_ = 0; + } + + return len; +} + +void SocketBIOAdapter::HandleSocketReadResult(int result) { + DCHECK_NE(ERR_IO_PENDING, result); + + // If an EOF, canonicalize to ERR_CONNECTION_CLOSED here, so that higher + // levels don't report success. + if (result == 0) + result = ERR_CONNECTION_CLOSED; + + read_result_ = result; + + // The read buffer is no longer needed. + if (read_result_ <= 0) + read_buffer_ = nullptr; +} + +void SocketBIOAdapter::OnSocketReadComplete(int result) { + DCHECK_EQ(ERR_IO_PENDING, read_result_); + + HandleSocketReadResult(result); + delegate_->OnReadReady(); +} + +int SocketBIOAdapter::BIOWrite(const char* in, int len) { + if (len <= 0) + return len; + + // If the write buffer is not empty, there must be a pending Write() to flush + // it. + DCHECK(write_buffer_used_ == 0 || write_error_ == ERR_IO_PENDING); + + // If a previous Write() failed, report the error. + if (write_error_ != OK && write_error_ != ERR_IO_PENDING) { + OpenSSLPutNetError(FROM_HERE, write_error_); + return -1; + } + + // Instantiate the write buffer if needed. + if (!write_buffer_) { + DCHECK_EQ(0, write_buffer_used_); + write_buffer_ = new GrowableIOBuffer; + write_buffer_->SetCapacity(write_buffer_capacity_); + } + + // If the ring buffer is full, inform the caller to try again later. + if (write_buffer_used_ == write_buffer_->capacity()) { + BIO_set_retry_write(bio()); + return -1; + } + + int bytes_copied = 0; + + // If there is space after the offset, fill it. + if (write_buffer_used_ < write_buffer_->RemainingCapacity()) { + int chunk = + std::min(write_buffer_->RemainingCapacity() - write_buffer_used_, len); + memcpy(write_buffer_->data() + write_buffer_used_, in, chunk); + in += chunk; + len -= chunk; + bytes_copied += chunk; + write_buffer_used_ += chunk; + } + + // If there is still space for remaining data, try to wrap around. + if (len > 0 && write_buffer_used_ < write_buffer_->capacity()) { + // If there were any room after the offset, the previous branch would have + // filled it. + CHECK_LE(write_buffer_->RemainingCapacity(), write_buffer_used_); + int write_offset = write_buffer_used_ - write_buffer_->RemainingCapacity(); + int chunk = std::min(len, write_buffer_->capacity() - write_buffer_used_); + memcpy(write_buffer_->StartOfBuffer() + write_offset, in, chunk); + in += chunk; + len -= chunk; + bytes_copied += chunk; + write_buffer_used_ += chunk; + } + + // Either the buffer is now full or there is no more input. + DCHECK(len == 0 || write_buffer_used_ == write_buffer_->capacity()); + + // Schedule a socket Write() if necessary. (The ring buffer may previously + // have been empty.) + SocketWrite(); + + // If a read-interrupting write error was synchronously discovered, + // asynchronously notify OnReadReady. See https://crbug.com/249848. Avoid + // reentrancy by deferring it to a later event loop iteration. + if (write_error_ != OK && write_error_ != ERR_IO_PENDING && + read_result_ == ERR_IO_PENDING) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&SocketBIOAdapter::CallOnReadReady, + weak_factory_.GetWeakPtr())); + } + + return bytes_copied; +} + +void SocketBIOAdapter::SocketWrite() { + while (write_error_ == OK && write_buffer_used_ > 0) { + int write_size = + std::min(write_buffer_used_, write_buffer_->RemainingCapacity()); + int result = + socket_->Write(write_buffer_.get(), write_size, write_callback_); + if (result == ERR_IO_PENDING) { + write_error_ = ERR_IO_PENDING; + return; + } + + HandleSocketWriteResult(result); + } +} + +void SocketBIOAdapter::HandleSocketWriteResult(int result) { + DCHECK_NE(ERR_IO_PENDING, result); + + if (result < 0) { + write_error_ = result; + + // The write buffer is no longer needed. + write_buffer_ = nullptr; + write_buffer_used_ = 0; + return; + } + + // Advance the ring buffer. + write_buffer_->set_offset(write_buffer_->offset() + result); + write_buffer_used_ -= result; + if (write_buffer_->RemainingCapacity() == 0) + write_buffer_->set_offset(0); + write_error_ = OK; + + // Release the write buffer if empty. + if (write_buffer_used_ == 0) + write_buffer_ = nullptr; +} + +void SocketBIOAdapter::OnSocketWriteComplete(int result) { + DCHECK_EQ(ERR_IO_PENDING, write_error_); + + bool was_full = write_buffer_used_ == write_buffer_->capacity(); + + HandleSocketWriteResult(result); + SocketWrite(); + + // If transitioning from being unable to accept data to being able to, signal + // OnWriteReady. + if (was_full) { + base::WeakPtr<SocketBIOAdapter> guard(weak_factory_.GetWeakPtr()); + delegate_->OnWriteReady(); + // OnWriteReady may delete the adapter. + if (!guard) + return; + } + + // Write errors are fed back into BIO_read once the read buffer is empty. If + // BIO_read is currently blocked, signal early that a read result is ready. + if (result < 0 && read_result_ == ERR_IO_PENDING) + delegate_->OnReadReady(); +} + +void SocketBIOAdapter::CallOnReadReady() { + if (read_result_ == ERR_IO_PENDING) + delegate_->OnReadReady(); +} + +SocketBIOAdapter* SocketBIOAdapter::GetAdapter(BIO* bio) { + DCHECK_EQ(&kBIOMethod, bio->method); + SocketBIOAdapter* adapter = reinterpret_cast<SocketBIOAdapter*>(bio->ptr); + if (adapter) + DCHECK_EQ(bio, adapter->bio()); + return adapter; +} + +int SocketBIOAdapter::BIOWriteWrapper(BIO* bio, const char* in, int len) { + BIO_clear_retry_flags(bio); + + SocketBIOAdapter* adapter = GetAdapter(bio); + if (!adapter) { + OpenSSLPutNetError(FROM_HERE, ERR_UNEXPECTED); + return -1; + } + + return adapter->BIOWrite(in, len); +} + +int SocketBIOAdapter::BIOReadWrapper(BIO* bio, char* out, int len) { + BIO_clear_retry_flags(bio); + + SocketBIOAdapter* adapter = GetAdapter(bio); + if (!adapter) { + OpenSSLPutNetError(FROM_HERE, ERR_UNEXPECTED); + return -1; + } + + return adapter->BIORead(out, len); +} + +long SocketBIOAdapter::BIOCtrlWrapper(BIO* bio, + int cmd, + long larg, + void* parg) { + switch (cmd) { + case BIO_CTRL_FLUSH: + // The SSL stack requires BIOs handle BIO_flush. + return 1; + } + + NOTIMPLEMENTED(); + return 0; +} + +const BIO_METHOD SocketBIOAdapter::kBIOMethod = { + 0, // type (unused) + nullptr, // name (unused) + SocketBIOAdapter::BIOWriteWrapper, + SocketBIOAdapter::BIOReadWrapper, + nullptr, // puts + nullptr, // gets + SocketBIOAdapter::BIOCtrlWrapper, + nullptr, // create + nullptr, // destroy + nullptr, // callback_ctrl +}; + +} // namespace net
diff --git a/net/socket/socket_bio_adapter.h b/net/socket/socket_bio_adapter.h new file mode 100644 index 0000000..d4c8172 --- /dev/null +++ b/net/socket/socket_bio_adapter.h
@@ -0,0 +1,143 @@ +// 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 NET_SOCKET_SOCKET_BIO_ADAPTER_H_ +#define NET_SOCKET_SOCKET_BIO_ADAPTER_H_ + +#include <openssl/base.h> + +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "net/base/completion_callback.h" +#include "net/base/net_export.h" + +namespace net { + +class GrowableIOBuffer; +class IOBuffer; +class StreamSocket; + +// An adapter to convert between StreamSocket and OpenSSL BIO I/O models. +// +// BIO exposes a UNIX-like interface where BIO_read and BIO_write may either +// succeed synchronously or be retried (with no memory between calls). +// StreamSocket exposes an asynchronous interface where an asynchronous +// operation continues running and completes with a callback. +// +// For reading, SocketBIOAdapter maintains a buffer to pass to +// StreamSocket::Read. Once that Read completes, BIO_read synchronously drains +// the buffer and signals BIO_should_read once empty. +// +// For writing, SocketBIOAdapter maintains a ring buffer of data to be written +// to the StreamSocket. BIO_write synchronously copies data into the buffer or +// signals BIO_should_write if the buffer is full. The ring buffer is drained +// asynchronously into the socket. Note this means write errors are reported at +// a later BIO_write. +// +// To work around this delay, write errors are also surfaced out of +// BIO_read. Otherwise a failure in the final BIO_write of an application may go +// unnoticed. If this occurs, OnReadReady will be signaled as if it were a read +// error. See https://crbug.com/249848. +class NET_EXPORT_PRIVATE SocketBIOAdapter { + public: + // A delegate interface for when the sockets are ready. BIO assumes external + // knowledge of when to retry operations (such as a select() loop for UNIX), + // which is signaled out of StreamSocket's callbacks here. + // + // Callers should implement these methods and, when signaled, retry the + // BIO_read or BIO_write. This usually is done by retrying a higher-level + // operation, such as SSL_read or SSL_write. + // + // Callers may assume that OnReadReady and OnWriteReady will only be called + // from a PostTask or StreamSocket callback. + class Delegate { + public: + // Called when the BIO is ready to handle BIO_read, after having previously + // been blocked. + virtual void OnReadReady() = 0; + + // Called when the BIO is ready to handle BIO_write, after having previously + // been blocked. + virtual void OnWriteReady() = 0; + + protected: + virtual ~Delegate() {} + }; + + // Creates a new SocketBIOAdapter for the specified socket. |socket| and + // |delegate| must remain valid for the lifetime of the SocketBIOAdapter. + SocketBIOAdapter(StreamSocket* socket, + int read_buffer_capacity, + int write_buffer_capacity, + Delegate* delegate); + ~SocketBIOAdapter(); + + BIO* bio() { return bio_.get(); } + + // Returns true if any data has been read from the underlying StreamSocket, + // but not yet consumed by the BIO. + bool HasPendingReadData(); + + private: + int BIORead(char* out, int len); + void HandleSocketReadResult(int result); + void OnSocketReadComplete(int result); + + int BIOWrite(const char* in, int len); + void SocketWrite(); + void HandleSocketWriteResult(int result); + void OnSocketWriteComplete(int result); + void CallOnReadReady(); + + static SocketBIOAdapter* GetAdapter(BIO* bio); + static int BIOReadWrapper(BIO* bio, char* out, int len); + static int BIOWriteWrapper(BIO* bio, const char* in, int len); + static long BIOCtrlWrapper(BIO* bio, int cmd, long larg, void* parg); + + static const BIO_METHOD kBIOMethod; + + bssl::UniquePtr<BIO> bio_; + + // The pointer is non-owning so this class may be used with both + // ClientSocketHandles and raw StreamSockets. + StreamSocket* socket_; + + CompletionCallback read_callback_; + CompletionCallback write_callback_; + + // The capacity of the read buffer. + int read_buffer_capacity_; + // A buffer containing data from the most recent socket Read(). The buffer is + // deallocated when unused. + scoped_refptr<IOBuffer> read_buffer_; + // The number of bytes of read_buffer_ consumed. + int read_offset_; + // The result of the most recent socket Read(). If ERR_IO_PENDING, there is a + // socket Read() in progress. If another error, Read() has failed. Otherwise, + // it is the number of bytes in the buffer (zero if empty). + int read_result_; + + // The capacity of the write buffer. + int write_buffer_capacity_; + // A ring buffer of data to be written to the transport. The offset of the + // buffer is the start of the ring buffer and is advanced on successful + // Write(). The buffer is deallocated when unused. + scoped_refptr<GrowableIOBuffer> write_buffer_; + // The number of bytes of data in write_buffer_. + int write_buffer_used_; + // The most recent socket Write() error. If ERR_IO_PENDING, there is a socket + // Write() in progress. If OK, there is no socket Write() in progress and none + // have failed. + int write_error_; + + Delegate* delegate_; + + base::WeakPtrFactory<SocketBIOAdapter> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(SocketBIOAdapter); +}; + +} // namespace net + +#endif // NET_SOCKET_SOCKET_BIO_ADAPTER_H_
diff --git a/net/socket/socket_bio_adapter_unittest.cc b/net/socket/socket_bio_adapter_unittest.cc new file mode 100644 index 0000000..455f8fb --- /dev/null +++ b/net/socket/socket_bio_adapter_unittest.cc
@@ -0,0 +1,617 @@ +// 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 "net/socket/socket_bio_adapter.h" + +#include <openssl/bio.h> +#include <openssl/err.h> +#include <openssl/ssl.h> +#include <string.h> + +#include <memory> + +#include "base/location.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/run_loop.h" +#include "crypto/openssl_util.h" +#include "net/base/address_list.h" +#include "net/base/net_errors.h" +#include "net/log/net_log_source.h" +#include "net/socket/socket_test_util.h" +#include "net/socket/stream_socket.h" +#include "net/ssl/openssl_ssl_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +class SocketBIOAdapterTest : public testing::Test, + public SocketBIOAdapter::Delegate { + protected: + std::unique_ptr<StreamSocket> MakeTestSocket(SocketDataProvider* data) { + data->set_connect_data(MockConnect(SYNCHRONOUS, OK)); + factory_.AddSocketDataProvider(data); + std::unique_ptr<StreamSocket> socket = factory_.CreateTransportClientSocket( + AddressList(), nullptr, nullptr, NetLogSource()); + CHECK_EQ(OK, socket->Connect(net::CompletionCallback())); + return socket; + } + + void set_reset_on_write_ready( + std::unique_ptr<SocketBIOAdapter>* reset_on_write_ready) { + reset_on_write_ready_ = reset_on_write_ready; + } + + void ExpectReadError(BIO* bio, + int error, + const crypto::OpenSSLErrStackTracer& tracer) { + // BIO_read should fail. + char buf; + EXPECT_EQ(-1, BIO_read(bio, &buf, 1)); + EXPECT_EQ(error, MapOpenSSLError(SSL_ERROR_SSL, tracer)); + EXPECT_FALSE(BIO_should_read(bio)); + + // Repeating the operation should replay the error. + EXPECT_EQ(-1, BIO_read(bio, &buf, 1)); + EXPECT_EQ(error, MapOpenSSLError(SSL_ERROR_SSL, tracer)); + EXPECT_FALSE(BIO_should_read(bio)); + } + + void ExpectBlockingRead(BIO* bio, void* buf, int len) { + // BIO_read should return a retryable error. + EXPECT_EQ(-1, BIO_read(bio, buf, len)); + EXPECT_TRUE(BIO_should_read(bio)); + EXPECT_EQ(0u, ERR_peek_error()); + + // Repeating the operation has the same result. + EXPECT_EQ(-1, BIO_read(bio, buf, len)); + EXPECT_TRUE(BIO_should_read(bio)); + EXPECT_EQ(0u, ERR_peek_error()); + } + + void ExpectWriteError(BIO* bio, + int error, + const crypto::OpenSSLErrStackTracer& tracer) { + // BIO_write should fail. + char buf = '?'; + EXPECT_EQ(-1, BIO_write(bio, &buf, 1)); + EXPECT_EQ(error, MapOpenSSLError(SSL_ERROR_SSL, tracer)); + EXPECT_FALSE(BIO_should_write(bio)); + + // Repeating the operation should replay the error. + EXPECT_EQ(-1, BIO_write(bio, &buf, 1)); + EXPECT_EQ(error, MapOpenSSLError(SSL_ERROR_SSL, tracer)); + EXPECT_FALSE(BIO_should_write(bio)); + } + + void ExpectBlockingWrite(BIO* bio, const void* buf, int len) { + // BIO_write should return a retryable error. + EXPECT_EQ(-1, BIO_write(bio, buf, len)); + EXPECT_TRUE(BIO_should_write(bio)); + EXPECT_EQ(0u, ERR_peek_error()); + + // Repeating the operation has the same result. + EXPECT_EQ(-1, BIO_write(bio, buf, len)); + EXPECT_TRUE(BIO_should_write(bio)); + EXPECT_EQ(0u, ERR_peek_error()); + } + + void WaitForReadReady() { + expect_read_ready_ = true; + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(expect_read_ready_); + } + + void WaitForWriteReady(SequencedSocketData* to_resume) { + expect_write_ready_ = true; + if (to_resume) { + to_resume->Resume(); + } + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(expect_write_ready_); + } + + void WaitForBothReady() { + expect_read_ready_ = true; + expect_write_ready_ = true; + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(expect_read_ready_); + EXPECT_FALSE(expect_write_ready_); + } + + // SocketBIOAdapter::Delegate implementation: + void OnReadReady() override { + EXPECT_TRUE(expect_read_ready_); + expect_read_ready_ = false; + } + + void OnWriteReady() override { + EXPECT_TRUE(expect_write_ready_); + expect_write_ready_ = false; + if (reset_on_write_ready_) + reset_on_write_ready_->reset(); + } + + private: + bool expect_read_ready_ = false; + bool expect_write_ready_ = false; + MockClientSocketFactory factory_; + std::unique_ptr<SocketBIOAdapter>* reset_on_write_ready_ = nullptr; +}; + +// Test that data can be read synchronously. +TEST_F(SocketBIOAdapterTest, ReadSync) { + crypto::OpenSSLErrStackTracer tracer(FROM_HERE); + + MockRead reads[] = { + MockRead(SYNCHRONOUS, 0, "hello"), MockRead(SYNCHRONOUS, 1, "world"), + MockRead(SYNCHRONOUS, ERR_CONNECTION_RESET, 2), + }; + + SequencedSocketData data(reads, arraysize(reads), nullptr, 0); + std::unique_ptr<StreamSocket> socket = MakeTestSocket(&data); + std::unique_ptr<SocketBIOAdapter> adapter = + base::MakeUnique<SocketBIOAdapter>(socket.get(), 100, 100, this); + BIO* bio = adapter->bio(); + EXPECT_FALSE(adapter->HasPendingReadData()); + + // Read the data synchronously. Although the buffer has room for both, + // BIO_read only reports one socket-level Read. + char buf[10]; + EXPECT_EQ(5, BIO_read(bio, buf, sizeof(buf))); + EXPECT_EQ(0, memcmp("hello", buf, 5)); + EXPECT_FALSE(adapter->HasPendingReadData()); + + // Consume the next portion one byte at a time. + EXPECT_EQ(1, BIO_read(bio, buf, 1)); + EXPECT_EQ('w', buf[0]); + EXPECT_TRUE(adapter->HasPendingReadData()); + + EXPECT_EQ(1, BIO_read(bio, buf, 1)); + EXPECT_EQ('o', buf[0]); + EXPECT_TRUE(adapter->HasPendingReadData()); + + // The remainder may be consumed in a single BIO_read. + EXPECT_EQ(3, BIO_read(bio, buf, sizeof(buf))); + EXPECT_EQ(0, memcmp("rld", buf, 3)); + EXPECT_FALSE(adapter->HasPendingReadData()); + + // The error is available synchoronously. + ExpectReadError(bio, ERR_CONNECTION_RESET, tracer); +} + +// Test that data can be read asynchronously. +TEST_F(SocketBIOAdapterTest, ReadAsync) { + crypto::OpenSSLErrStackTracer tracer(FROM_HERE); + + MockRead reads[] = { + MockRead(ASYNC, 0, "hello"), MockRead(ASYNC, 1, "world"), + MockRead(ASYNC, ERR_CONNECTION_RESET, 2), + }; + + SequencedSocketData data(reads, arraysize(reads), nullptr, 0); + std::unique_ptr<StreamSocket> socket = MakeTestSocket(&data); + std::unique_ptr<SocketBIOAdapter> adapter = + base::MakeUnique<SocketBIOAdapter>(socket.get(), 100, 100, this); + BIO* bio = adapter->bio(); + EXPECT_FALSE(adapter->HasPendingReadData()); + + // Attempt to read data. It will fail but schedule a Read. + char buf[10]; + ExpectBlockingRead(bio, buf, sizeof(buf)); + EXPECT_FALSE(adapter->HasPendingReadData()); + + // After waiting, the data is available. + WaitForReadReady(); + EXPECT_TRUE(adapter->HasPendingReadData()); + + // The first read is now available synchronously. + EXPECT_EQ(5, BIO_read(bio, buf, sizeof(buf))); + EXPECT_EQ(0, memcmp("hello", buf, 5)); + EXPECT_FALSE(adapter->HasPendingReadData()); + + // The adapter does not schedule another Read until BIO_read is next called. + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(adapter->HasPendingReadData()); + + // This time, under-request the data. The adapter should still read the full + // amount. + ExpectBlockingRead(bio, buf, 1); + EXPECT_FALSE(adapter->HasPendingReadData()); + WaitForReadReady(); + EXPECT_TRUE(adapter->HasPendingReadData()); + + // The next read is now available synchronously. + EXPECT_EQ(5, BIO_read(bio, buf, sizeof(buf))); + EXPECT_EQ(0, memcmp("world", buf, 5)); + EXPECT_FALSE(adapter->HasPendingReadData()); + + // The error is not yet available. + ExpectBlockingRead(bio, buf, sizeof(buf)); + WaitForReadReady(); + + // The error is now available synchoronously. + ExpectReadError(bio, ERR_CONNECTION_RESET, tracer); +} + +// Test that synchronous EOF is mapped to ERR_CONNECTION_CLOSED. +TEST_F(SocketBIOAdapterTest, ReadEOFSync) { + crypto::OpenSSLErrStackTracer tracer(FROM_HERE); + + MockRead reads[] = { + MockRead(SYNCHRONOUS, 0, 0), + }; + + SequencedSocketData data(reads, arraysize(reads), nullptr, 0); + std::unique_ptr<StreamSocket> socket = MakeTestSocket(&data); + std::unique_ptr<SocketBIOAdapter> adapter = + base::MakeUnique<SocketBIOAdapter>(socket.get(), 100, 100, this); + + ExpectReadError(adapter->bio(), ERR_CONNECTION_CLOSED, tracer); +} + +// Test that asynchronous EOF is mapped to ERR_CONNECTION_CLOSED. +TEST_F(SocketBIOAdapterTest, ReadEOFAsync) { + crypto::OpenSSLErrStackTracer tracer(FROM_HERE); + + MockRead reads[] = { + MockRead(ASYNC, 0, 0), + }; + + SequencedSocketData data(reads, arraysize(reads), nullptr, 0); + std::unique_ptr<StreamSocket> socket = MakeTestSocket(&data); + std::unique_ptr<SocketBIOAdapter> adapter = + base::MakeUnique<SocketBIOAdapter>(socket.get(), 100, 100, this); + + char buf; + ExpectBlockingRead(adapter->bio(), &buf, 1); + WaitForReadReady(); + ExpectReadError(adapter->bio(), ERR_CONNECTION_CLOSED, tracer); +} + +// Test that data can be written synchronously. +TEST_F(SocketBIOAdapterTest, WriteSync) { + crypto::OpenSSLErrStackTracer tracer(FROM_HERE); + + MockWrite writes[] = { + MockWrite(SYNCHRONOUS, 0, "hello"), + MockWrite(SYNCHRONOUS, 1, "wor"), + MockWrite(SYNCHRONOUS, 2, "ld"), + MockWrite(SYNCHRONOUS, 3, "helloworld"), + MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET, 4), + }; + + SequencedSocketData data(nullptr, 0, writes, arraysize(writes)); + std::unique_ptr<StreamSocket> socket = MakeTestSocket(&data); + std::unique_ptr<SocketBIOAdapter> adapter = + base::MakeUnique<SocketBIOAdapter>(socket.get(), 10, 10, this); + BIO* bio = adapter->bio(); + + // Test data entering and leaving the buffer synchronously. The second write + // takes multiple iterations (events 0 to 2). + EXPECT_EQ(5, BIO_write(bio, "hello", 5)); + EXPECT_EQ(5, BIO_write(bio, "world", 5)); + + // If writing larger than the buffer size, only part of the data is written + // (event 3). + EXPECT_EQ(10, BIO_write(bio, "helloworldhelloworld", 20)); + + // Writing "aaaaa" fails (event 4), but there is a write buffer, so errors + // are delayed. + EXPECT_EQ(5, BIO_write(bio, "aaaaa", 5)); + + // However once the error is registered, subsequent writes fail. + ExpectWriteError(bio, ERR_CONNECTION_RESET, tracer); +} + +// Test that data can be written asynchronously. +TEST_F(SocketBIOAdapterTest, WriteAsync) { + crypto::OpenSSLErrStackTracer tracer(FROM_HERE); + + MockWrite writes[] = { + MockWrite(ASYNC, 0, "aaa"), + MockWrite(ASYNC, ERR_IO_PENDING, 1), // pause + MockWrite(ASYNC, 2, "aabbbbb"), + MockWrite(ASYNC, 3, "ccc"), + MockWrite(ASYNC, 4, "ddd"), + MockWrite(ASYNC, ERR_IO_PENDING, 5), // pause + MockWrite(ASYNC, 6, "dd"), + MockWrite(SYNCHRONOUS, 7, "e"), + MockWrite(SYNCHRONOUS, 8, "e"), + MockWrite(ASYNC, 9, "e"), + MockWrite(ASYNC, 10, "ee"), + MockWrite(ASYNC, ERR_IO_PENDING, 11), // pause + MockWrite(ASYNC, 12, "eff"), + MockWrite(ASYNC, 13, "ggggggg"), + MockWrite(ASYNC, ERR_CONNECTION_RESET, 14), + }; + + SequencedSocketData data(nullptr, 0, writes, arraysize(writes)); + std::unique_ptr<StreamSocket> socket = MakeTestSocket(&data); + std::unique_ptr<SocketBIOAdapter> adapter = + base::MakeUnique<SocketBIOAdapter>(socket.get(), 10, 10, this); + BIO* bio = adapter->bio(); + + // Data which fits in the buffer is returned synchronously, even if not + // flushed synchronously. + EXPECT_EQ(5, BIO_write(bio, "aaaaa", 5)); + EXPECT_EQ(5, BIO_write(bio, "bbbbb", 5)); + + // The buffer contains: + // + // [aaaaabbbbb] + // ^ + + // The buffer is full now, so the next write will block. + ExpectBlockingWrite(bio, "zzzzz", 5); + + // Let the first socket write complete (event 0) and pause (event 1). + WaitForWriteReady(nullptr); + EXPECT_TRUE(data.IsPaused()); + + // The buffer contains: + // + // [...aabbbbb] + // ^ + + // The ring buffer now has 3 bytes of space with "aabbbbb" still to be + // written. Attempting to write 3 bytes means 3 succeed. + EXPECT_EQ(3, BIO_write(bio, "cccccccccc", 10)); + + // The buffer contains: + // + // [cccaabbbbb] + // ^ + + // Drain the buffer (events 2 and 3). + WaitForWriteReady(&data); + + // The buffer is now empty. + + // Now test something similar but arrange for a BIO_write (the 'e's below) to + // wrap around the buffer. Write five bytes into the buffer, flush the first + // three (event 4), and pause (event 5). OnWriteReady is not signaled because + // the buffer was not full. + EXPECT_EQ(5, BIO_write(bio, "ddddd", 5)); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(data.IsPaused()); + + // The buffer contains: + // + // [...dd.....] + // ^ + + // The adapter maintains a ring buffer, so 6 bytes fit. + EXPECT_EQ(6, BIO_write(bio, "eeeeee", 6)); + + // The buffer contains: + // + // [e..ddeeeee] + // ^ + + // The remaining space may be filled in. + EXPECT_EQ(2, BIO_write(bio, "ffffffffff", 10)); + + // The buffer contains: + // + // [effddeeeee] + // ^ + + // Drain to the end of the ring buffer, so it wraps around (events 6 to 10) + // and pause (event 11). Test that synchronous and asynchronous writes both + // drain. The start of the buffer has now wrapped around. + WaitForWriteReady(&data); + EXPECT_TRUE(data.IsPaused()); + + // The buffer contains: + // + // [eff.......] + // ^ + + // Test wrapping around works correctly and the buffer may be appended to. + EXPECT_EQ(7, BIO_write(bio, "gggggggggg", 10)); + + // The buffer contains: + // + // [effggggggg] + // ^ + + // The buffer is full now, so the next write will block. + ExpectBlockingWrite(bio, "zzzzz", 5); + + // Drain the buffer to confirm the ring buffer's contents are as expected + // (events 12 and 13). + WaitForWriteReady(&data); + + // Write again so the write error may be discovered. + EXPECT_EQ(5, BIO_write(bio, "hhhhh", 5)); + + // Release the write error (event 14). At this point future BIO_write calls + // fail. The buffer was not full, so OnWriteReady is not signalled. + base::RunLoop().RunUntilIdle(); + ExpectWriteError(bio, ERR_CONNECTION_RESET, tracer); +} + +// Test that a failed socket write is reported through BIO_read and prevents it +// from scheduling a socket read. See https://crbug.com/249848. +TEST_F(SocketBIOAdapterTest, WriteStopsRead) { + crypto::OpenSSLErrStackTracer tracer(FROM_HERE); + + MockWrite writes[] = { + MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET, 0), + }; + + SequencedSocketData data(nullptr, 0, writes, arraysize(writes)); + std::unique_ptr<StreamSocket> socket = MakeTestSocket(&data); + std::unique_ptr<SocketBIOAdapter> adapter = + base::MakeUnique<SocketBIOAdapter>(socket.get(), 100, 100, this); + BIO* bio = adapter->bio(); + + // The write fails, but there is a write buffer, so errors are delayed. + EXPECT_EQ(5, BIO_write(bio, "aaaaa", 5)); + + // The write error is surfaced out of BIO_read. There are no MockReads, so + // this also tests that no socket reads are attempted. + ExpectReadError(bio, ERR_CONNECTION_RESET, tracer); +} + +// Test that a synchronous failed socket write interrupts a blocked +// BIO_read. See https://crbug.com/249848. +TEST_F(SocketBIOAdapterTest, SyncWriteInterruptsRead) { + crypto::OpenSSLErrStackTracer tracer(FROM_HERE); + + MockRead reads[] = { + MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0), + }; + + MockWrite writes[] = { + MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET, 1), + }; + + SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); + std::unique_ptr<StreamSocket> socket = MakeTestSocket(&data); + std::unique_ptr<SocketBIOAdapter> adapter = + base::MakeUnique<SocketBIOAdapter>(socket.get(), 100, 100, this); + BIO* bio = adapter->bio(); + + // Attempt to read from the transport. It will block indefinitely. + char buf; + ExpectBlockingRead(adapter->bio(), &buf, 1); + + // Schedule a socket write. + EXPECT_EQ(5, BIO_write(bio, "aaaaa", 5)); + + // The write error triggers OnReadReady. + WaitForReadReady(); + + // The write error is surfaced out of BIO_read. + ExpectReadError(bio, ERR_CONNECTION_RESET, tracer); +} + +// Test that an asynchronous failed socket write interrupts a blocked +// BIO_read. See https://crbug.com/249848. +TEST_F(SocketBIOAdapterTest, AsyncWriteInterruptsRead) { + crypto::OpenSSLErrStackTracer tracer(FROM_HERE); + + MockRead reads[] = { + MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0), + }; + + MockWrite writes[] = { + MockWrite(ASYNC, ERR_CONNECTION_RESET, 1), + }; + + SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); + std::unique_ptr<StreamSocket> socket = MakeTestSocket(&data); + std::unique_ptr<SocketBIOAdapter> adapter = + base::MakeUnique<SocketBIOAdapter>(socket.get(), 100, 100, this); + BIO* bio = adapter->bio(); + + // Attempt to read from the transport. It will block indefinitely. + char buf; + ExpectBlockingRead(adapter->bio(), &buf, 1); + + // Schedule a socket write. + EXPECT_EQ(5, BIO_write(bio, "aaaaa", 5)); + + // The write error is signaled asynchronously and interrupts BIO_read, so + // OnReadReady is signaled. The write buffer was not full, so OnWriteReady is + // not signaled. + WaitForReadReady(); + + // The write error is surfaced out of BIO_read. + ExpectReadError(bio, ERR_CONNECTION_RESET, tracer); +} + +// Test that an asynchronous failed socket write interrupts a blocked BIO_read, +// signaling both if the buffer was full. See https://crbug.com/249848. +TEST_F(SocketBIOAdapterTest, AsyncWriteInterruptsBoth) { + crypto::OpenSSLErrStackTracer tracer(FROM_HERE); + + MockRead reads[] = { + MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0), + }; + + MockWrite writes[] = { + MockWrite(ASYNC, ERR_CONNECTION_RESET, 1), + }; + + SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); + std::unique_ptr<StreamSocket> socket = MakeTestSocket(&data); + std::unique_ptr<SocketBIOAdapter> adapter = + base::MakeUnique<SocketBIOAdapter>(socket.get(), 5, 5, this); + BIO* bio = adapter->bio(); + + // Attempt to read from the transport. It will block indefinitely. + char buf; + ExpectBlockingRead(adapter->bio(), &buf, 1); + + // Schedule a socket write. + EXPECT_EQ(5, BIO_write(bio, "aaaaa", 5)); + + // The write error is signaled asynchronously and interrupts BIO_read, so + // OnReadReady is signaled. The write buffer was full, so both OnWriteReady is + // also signaled. + WaitForBothReady(); + + // The write error is surfaced out of BIO_read. + ExpectReadError(bio, ERR_CONNECTION_RESET, tracer); +} + +// Test that SocketBIOAdapter handles OnWriteReady deleting itself when both +// need to be signaled. +TEST_F(SocketBIOAdapterTest, DeleteOnWriteReady) { + crypto::OpenSSLErrStackTracer tracer(FROM_HERE); + + MockRead reads[] = { + MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0), + }; + + MockWrite writes[] = { + MockWrite(ASYNC, ERR_CONNECTION_RESET, 1), + }; + + SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); + std::unique_ptr<StreamSocket> socket = MakeTestSocket(&data); + std::unique_ptr<SocketBIOAdapter> adapter = + base::MakeUnique<SocketBIOAdapter>(socket.get(), 5, 5, this); + BIO* bio = adapter->bio(); + + // Arrange for OnReadReady and OnWriteReady to both be signaled due to write + // error propagation (see the AsyncWriteInterruptsBoth test). + char buf; + ExpectBlockingRead(adapter->bio(), &buf, 1); + EXPECT_EQ(5, BIO_write(bio, "aaaaa", 5)); + + // Both OnWriteReady and OnReadReady would be signaled, but OnWriteReady + // deletes the adapter first. + set_reset_on_write_ready(&adapter); + WaitForWriteReady(nullptr); + + EXPECT_FALSE(adapter); +} + +// Test that using a BIO after the underlying adapter is destroyed fails +// gracefully. +TEST_F(SocketBIOAdapterTest, Detached) { + crypto::OpenSSLErrStackTracer tracer(FROM_HERE); + + SequencedSocketData data(nullptr, 0, nullptr, 0); + std::unique_ptr<StreamSocket> socket = MakeTestSocket(&data); + std::unique_ptr<SocketBIOAdapter> adapter = + base::MakeUnique<SocketBIOAdapter>(socket.get(), 100, 100, this); + + // Retain an additional reference to the BIO. + bssl::UniquePtr<BIO> bio(adapter->bio()); + BIO_up_ref(bio.get()); + + // Release the adapter. + adapter.reset(); + + ExpectReadError(bio.get(), ERR_UNEXPECTED, tracer); + ExpectWriteError(bio.get(), ERR_UNEXPECTED, tracer); +} + +} // namespace net
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc index 15c26f8..ef21477 100644 --- a/net/socket/socket_test_util.cc +++ b/net/socket/socket_test_util.cc
@@ -201,10 +201,16 @@ return writes_[write_index_++]; } +void StaticSocketDataHelper::Reset() { + read_index_ = 0; + write_index_ = 0; +} + bool StaticSocketDataHelper::VerifyWriteData(const std::string& data) { CHECK(!AllWriteDataConsumed()); - // Check that what the actual data matches the expectations. - const MockWrite& next_write = PeekWrite(); + // Check that the actual data matches the expectations, skipping over any + // pause events. + const MockWrite& next_write = PeekRealWrite(); if (!next_write.data) return true; @@ -221,9 +227,14 @@ return expected_data == actual_data; } -void StaticSocketDataHelper::Reset() { - read_index_ = 0; - write_index_ = 0; +const MockWrite& StaticSocketDataHelper::PeekRealWrite() const { + for (size_t i = write_index_; i < write_count_; i++) { + if (writes_[i].mode != ASYNC || writes_[i].result != ERR_IO_PENDING) + return writes_[i]; + } + + CHECK(false) << "No write data available."; + return writes_[0]; // Avoid warning about unreachable missing return. } StaticSocketDataProvider::StaticSocketDataProvider()
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h index 4cc20d6..b0bbd89 100644 --- a/net/socket/socket_test_util.h +++ b/net/socket/socket_test_util.h
@@ -280,12 +280,12 @@ size_t writes_count); ~StaticSocketDataHelper(); - // These functions get access to the next available read and write data, - // or null if there is no more data available. + // These functions get access to the next available read and write data. They + // CHECK fail if there is no data available. const MockRead& PeekRead() const; const MockWrite& PeekWrite() const; - // Returns the current read or write , and then advances to the next one. + // Returns the current read or write, and then advances to the next one. const MockRead& AdvanceRead(); const MockWrite& AdvanceWrite(); @@ -306,6 +306,10 @@ bool AllWriteDataConsumed() const { return write_index_ >= write_count_; } private: + // Returns the next available read or write that is not a pause event. CHECK + // fails if no data is available. + const MockWrite& PeekRealWrite() const; + MockRead* reads_; size_t read_index_; size_t read_count_; @@ -432,7 +436,7 @@ IDLE, // No async operation is in progress. PENDING, // An async operation in waiting for another opteration to // complete. - COMPLETING, // A task has been posted to complet an async operation. + COMPLETING, // A task has been posted to complete an async operation. PAUSED, // IO is paused until Resume() is called. };
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc index 69258e3..bb3df44 100644 --- a/net/socket/ssl_client_socket_impl.cc +++ b/net/socket/ssl_client_socket_impl.cc
@@ -200,6 +200,22 @@ return std::move(dict); } +int GetBufferSize(const char* field_trial) { + // Get buffer sizes from field trials, if possible. If values not present, + // use default. Also make sure values are in reasonable range. + int buffer_size = kDefaultOpenSSLBufferSize; +#if !defined(OS_NACL) + int override_buffer_size; + if (base::StringToInt(base::FieldTrialList::FindFullName(field_trial), + &override_buffer_size)) { + buffer_size = override_buffer_size; + buffer_size = std::max(buffer_size, 1000); + buffer_size = std::min(buffer_size, 2 * kDefaultOpenSSLBufferSize); + } +#endif // !defined(OS_NACL) + return buffer_size; +} + } // namespace class SSLClientSocketImpl::SSLContext { @@ -476,12 +492,8 @@ const HostPortPair& host_and_port, const SSLConfig& ssl_config, const SSLClientSocketContext& context) - : transport_send_busy_(false), - transport_recv_busy_(false), - pending_read_error_(kNoPendingResult), + : pending_read_error_(kNoPendingResult), pending_read_ssl_error_(SSL_ERROR_NONE), - transport_read_error_(OK), - transport_write_error_(OK), server_cert_chain_(new PeerCertificateChain(NULL)), completed_connect_(false), was_ever_used_(false), @@ -633,6 +645,7 @@ cert_verifier_request_.reset(); channel_id_request_.Cancel(); weak_factory_.InvalidateWeakPtrs(); + transport_adapter_.reset(); // Release user callbacks. user_connect_callback_.Reset(); @@ -670,11 +683,11 @@ // If there is data read from the network that has not yet been consumed, do // not treat the connection as idle. // - // Note that this does not check |BIO_pending|, whether there is ciphertext - // that has not yet been flushed to the network. |Write| returns early, so - // this can cause race conditions which cause a socket to not be treated - // reusable when it should be. See https://crbug.com/466147. - if (BIO_wpending(transport_bio_.get()) > 0) + // Note that this does not check whether there is ciphertext that has not yet + // been flushed to the network. |Write| returns early, so this can cause race + // conditions which cause a socket to not be treated reusable when it should + // be. See https://crbug.com/466147. + if (transport_adapter_->HasPendingReadData()) return false; return transport_->socket()->IsConnectedAndIdle(); @@ -778,7 +791,7 @@ user_read_buf_ = buf; user_read_buf_len_ = buf_len; - int rv = DoReadLoop(); + int rv = DoPayloadRead(); if (rv == ERR_IO_PENDING) { user_read_callback_ = callback; @@ -798,7 +811,7 @@ user_write_buf_ = buf; user_write_buf_len_ = buf_len; - int rv = DoWriteLoop(); + int rv = DoPayloadWrite(); if (rv == ERR_IO_PENDING) { user_write_callback_ = callback; @@ -820,9 +833,20 @@ return transport_->socket()->SetSendBufferSize(size); } +void SSLClientSocketImpl::OnReadReady() { + // During a renegotiation, either Read or Write calls may be blocked on a + // transport read. + RetryAllOperations(); +} + +void SSLClientSocketImpl::OnWriteReady() { + // During a renegotiation, either Read or Write calls may be blocked on a + // transport read. + RetryAllOperations(); +} + int SSLClientSocketImpl::Init() { DCHECK(!ssl_); - DCHECK(!transport_bio_); #if defined(USE_NSS_CERTS) if (ssl_config_.cert_io_enabled) { @@ -855,55 +879,16 @@ if (session) SSL_set_session(ssl_.get(), session.get()); - // Get read and write buffer sizes from field trials, if possible. If values - // not present, use default. Also make sure values are in reasonable range. - int send_buffer_size = kDefaultOpenSSLBufferSize; -#if !defined(OS_NACL) - int override_send_buffer_size; - if (base::StringToInt(base::FieldTrialList::FindFullName("SSLBufferSizeSend"), - &override_send_buffer_size)) { - send_buffer_size = override_send_buffer_size; - send_buffer_size = std::max(send_buffer_size, 1000); - send_buffer_size = - std::min(send_buffer_size, 2 * kDefaultOpenSSLBufferSize); - } -#endif // !defined(OS_NACL) - send_buffer_ = new GrowableIOBuffer(); - send_buffer_->SetCapacity(send_buffer_size); + transport_adapter_.reset(new SocketBIOAdapter( + transport_->socket(), GetBufferSize("SSLBufferSizeRecv"), + GetBufferSize("SSLBufferSizeSend"), this)); + BIO* transport_bio = transport_adapter_->bio(); - int recv_buffer_size = kDefaultOpenSSLBufferSize; -#if !defined(OS_NACL) - int override_recv_buffer_size; - if (base::StringToInt(base::FieldTrialList::FindFullName("SSLBufferSizeRecv"), - &override_recv_buffer_size)) { - recv_buffer_size = override_recv_buffer_size; - recv_buffer_size = std::max(recv_buffer_size, 1000); - recv_buffer_size = - std::min(recv_buffer_size, 2 * kDefaultOpenSSLBufferSize); - } -#endif // !defined(OS_NACL) - recv_buffer_ = new GrowableIOBuffer(); - recv_buffer_->SetCapacity(recv_buffer_size); + BIO_up_ref(transport_bio); // SSL_set0_rbio takes ownership. + SSL_set0_rbio(ssl_.get(), transport_bio); - BIO* ssl_bio = NULL; - - // SSLClientSocketImpl retains ownership of the BIO buffers. - BIO* transport_bio_raw; - if (!BIO_new_bio_pair_external_buf( - &ssl_bio, send_buffer_->capacity(), - reinterpret_cast<uint8_t*>(send_buffer_->data()), &transport_bio_raw, - recv_buffer_->capacity(), - reinterpret_cast<uint8_t*>(recv_buffer_->data()))) - return ERR_UNEXPECTED; - transport_bio_.reset(transport_bio_raw); - DCHECK(ssl_bio); - DCHECK(transport_bio_); - - // Install a callback on OpenSSL's end to plumb transport errors through. - BIO_set_callback(ssl_bio, &SSLClientSocketImpl::BIOCallback); - BIO_set_callback_arg(ssl_bio, reinterpret_cast<char*>(this)); - - SSL_set_bio(ssl_.get(), ssl_bio, ssl_bio); + BIO_up_ref(transport_bio); // SSL_set0_wbio takes ownership. + SSL_set0_wbio(ssl_.get(), transport_bio); DCHECK_LT(SSL3_VERSION, ssl_config_.version_min); DCHECK_LT(SSL3_VERSION, ssl_config_.version_max); @@ -1031,21 +1016,6 @@ base::ResetAndReturn(&user_write_callback_).Run(rv); } -bool SSLClientSocketImpl::DoTransportIO() { - bool network_moved = false; - int rv; - // Read and write as much data as possible. The loop is necessary because - // Write() may return synchronously. - do { - rv = BufferSend(); - if (rv != ERR_IO_PENDING && rv != 0) - network_moved = true; - } while (rv > 0); - if (transport_read_error_ == OK && BufferRecv() != ERR_IO_PENDING) - network_moved = true; - return network_moved; -} - // TODO(cbentzel): Remove including "base/threading/thread_local.h" and // g_first_run_completed once crbug.com/424386 is fixed. base::LazyInstance<base::ThreadLocalBoolean>::Leaky g_first_run_completed = @@ -1333,36 +1303,6 @@ } } -void SSLClientSocketImpl::OnSendComplete(int result) { - if (next_handshake_state_ == STATE_HANDSHAKE) { - // In handshake phase. - OnHandshakeIOComplete(result); - return; - } - - // During a renegotiation, a Read call may also be blocked on a transport - // write, so retry both operations. - PumpReadWriteEvents(); -} - -void SSLClientSocketImpl::OnRecvComplete(int result) { - TRACE_EVENT0("net", "SSLClientSocketImpl::OnRecvComplete"); - if (next_handshake_state_ == STATE_HANDSHAKE) { - // In handshake phase. - OnHandshakeIOComplete(result); - return; - } - - // Network layer received some data, check if client requested to read - // decrypted data. - if (!user_read_buf_.get()) - return; - - int rv = DoReadLoop(); - if (rv != ERR_IO_PENDING) - DoReadCallback(rv); -} - int SSLClientSocketImpl::DoHandshakeLoop(int last_io_result) { TRACE_EVENT0("net", "SSLClientSocketImpl::DoHandshakeLoop"); int rv = last_io_result; @@ -1401,40 +1341,10 @@ NOTREACHED() << "unexpected state" << state; break; } - - bool network_moved = DoTransportIO(); - if (network_moved && next_handshake_state_ == STATE_HANDSHAKE) { - // In general we exit the loop if rv is ERR_IO_PENDING. In this - // special case we keep looping even if rv is ERR_IO_PENDING because - // the transport IO may allow DoHandshake to make progress. - rv = OK; // This causes us to stay in the loop. - } } while (rv != ERR_IO_PENDING && next_handshake_state_ != STATE_NONE); return rv; } -int SSLClientSocketImpl::DoReadLoop() { - bool network_moved; - int rv; - do { - rv = DoPayloadRead(); - network_moved = DoTransportIO(); - } while (rv == ERR_IO_PENDING && network_moved); - - return rv; -} - -int SSLClientSocketImpl::DoWriteLoop() { - bool network_moved; - int rv; - do { - rv = DoPayloadWrite(); - network_moved = DoTransportIO(); - } while (rv == ERR_IO_PENDING && network_moved); - - return rv; -} - int SSLClientSocketImpl::DoPayloadRead() { crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); @@ -1511,9 +1421,7 @@ // Do not treat insufficient data as an error to return in the next call to // DoPayloadRead() - instead, let the call fall through to check SSL_read() - // again. This is because DoTransportIO() may complete in between the next - // call to DoPayloadRead(), and thus it is important to check SSL_read() on - // subsequent invocations to see if a complete record may now be read. + // again. The transport may have data available by then. if (pending_read_error_ == ERR_IO_PENDING) pending_read_error_ = kNoPendingResult; } else { @@ -1561,158 +1469,38 @@ return net_error; } -void SSLClientSocketImpl::PumpReadWriteEvents() { +void SSLClientSocketImpl::RetryAllOperations() { + // SSL_do_handshake, SSL_read, and SSL_write may all be retried when blocked, + // so retry all operations for simplicity. (Otherwise, SSL_get_error for each + // operation may be remembered to retry only the blocked ones.) + + if (next_handshake_state_ == STATE_HANDSHAKE) { + // In handshake phase. The parameter to OnHandshakeIOComplete is unused. + OnHandshakeIOComplete(OK); + return; + } + int rv_read = ERR_IO_PENDING; int rv_write = ERR_IO_PENDING; - bool network_moved; - do { - if (user_read_buf_.get()) - rv_read = DoPayloadRead(); - if (user_write_buf_.get()) - rv_write = DoPayloadWrite(); - network_moved = DoTransportIO(); - } while (rv_read == ERR_IO_PENDING && rv_write == ERR_IO_PENDING && - (user_read_buf_.get() || user_write_buf_.get()) && network_moved); + if (user_read_buf_) + rv_read = DoPayloadRead(); + if (user_write_buf_) + rv_write = DoPayloadWrite(); // Performing the Read callback may cause |this| to be deleted. If this // happens, the Write callback should not be invoked. Guard against this by // holding a WeakPtr to |this| and ensuring it's still valid. base::WeakPtr<SSLClientSocketImpl> guard(weak_factory_.GetWeakPtr()); - if (user_read_buf_.get() && rv_read != ERR_IO_PENDING) + if (rv_read != ERR_IO_PENDING) DoReadCallback(rv_read); if (!guard.get()) return; - if (user_write_buf_.get() && rv_write != ERR_IO_PENDING) + if (rv_write != ERR_IO_PENDING) DoWriteCallback(rv_write); } -int SSLClientSocketImpl::BufferSend(void) { - if (transport_send_busy_) - return ERR_IO_PENDING; - - size_t buffer_read_offset; - uint8_t* read_buf; - size_t max_read; - int status = BIO_zero_copy_get_read_buf(transport_bio_.get(), &read_buf, - &buffer_read_offset, &max_read); - DCHECK_EQ(status, 1); // Should never fail. - if (!max_read) - return 0; // Nothing pending in the OpenSSL write BIO. - CHECK_EQ(read_buf, reinterpret_cast<uint8_t*>(send_buffer_->StartOfBuffer())); - CHECK_LT(buffer_read_offset, static_cast<size_t>(send_buffer_->capacity())); - send_buffer_->set_offset(buffer_read_offset); - - int rv = transport_->socket()->Write( - send_buffer_.get(), max_read, - base::Bind(&SSLClientSocketImpl::BufferSendComplete, - base::Unretained(this))); - if (rv == ERR_IO_PENDING) { - transport_send_busy_ = true; - } else { - TransportWriteComplete(rv); - } - return rv; -} - -int SSLClientSocketImpl::BufferRecv(void) { - if (transport_recv_busy_) - return ERR_IO_PENDING; - - // Determine how much was requested from |transport_bio_| that was not - // actually available. - size_t requested = BIO_ctrl_get_read_request(transport_bio_.get()); - if (requested == 0) { - // This is not a perfect match of error codes, as no operation is - // actually pending. However, returning 0 would be interpreted as - // a possible sign of EOF, which is also an inappropriate match. - return ERR_IO_PENDING; - } - - // Known Issue: While only reading |requested| data is the more correct - // implementation, it has the downside of resulting in frequent reads: - // One read for the SSL record header (~5 bytes) and one read for the SSL - // record body. Rather than issuing these reads to the underlying socket - // (and constantly allocating new IOBuffers), a single Read() request to - // fill |transport_bio_| is issued. As long as an SSL client socket cannot - // be gracefully shutdown (via SSL close alerts) and re-used for non-SSL - // traffic, this over-subscribed Read()ing will not cause issues. - - size_t buffer_write_offset; - uint8_t* write_buf; - size_t max_write; - int status = BIO_zero_copy_get_write_buf(transport_bio_.get(), &write_buf, - &buffer_write_offset, &max_write); - DCHECK_EQ(status, 1); // Should never fail. - if (!max_write) - return ERR_IO_PENDING; - - CHECK_EQ(write_buf, - reinterpret_cast<uint8_t*>(recv_buffer_->StartOfBuffer())); - CHECK_LT(buffer_write_offset, static_cast<size_t>(recv_buffer_->capacity())); - - recv_buffer_->set_offset(buffer_write_offset); - int rv = transport_->socket()->Read( - recv_buffer_.get(), max_write, - base::Bind(&SSLClientSocketImpl::BufferRecvComplete, - base::Unretained(this))); - if (rv == ERR_IO_PENDING) { - transport_recv_busy_ = true; - } else { - rv = TransportReadComplete(rv); - } - return rv; -} - -void SSLClientSocketImpl::BufferSendComplete(int result) { - TransportWriteComplete(result); - OnSendComplete(result); -} - -void SSLClientSocketImpl::BufferRecvComplete(int result) { - result = TransportReadComplete(result); - OnRecvComplete(result); -} - -void SSLClientSocketImpl::TransportWriteComplete(int result) { - DCHECK(ERR_IO_PENDING != result); - int bytes_written = 0; - if (result < 0) { - // Record the error. Save it to be reported in a future read or write on - // transport_bio_'s peer. - transport_write_error_ = result; - } else { - bytes_written = result; - } - DCHECK_GE(send_buffer_->RemainingCapacity(), bytes_written); - int ret = - BIO_zero_copy_get_read_buf_done(transport_bio_.get(), bytes_written); - DCHECK_EQ(1, ret); - transport_send_busy_ = false; -} - -int SSLClientSocketImpl::TransportReadComplete(int result) { - DCHECK(ERR_IO_PENDING != result); - // If an EOF, canonicalize to ERR_CONNECTION_CLOSED here so MapOpenSSLError - // does not report success. - if (result == 0) - result = ERR_CONNECTION_CLOSED; - int bytes_read = 0; - if (result < 0) { - // Received an error. Save it to be reported in a future read on - // transport_bio_'s peer. - transport_read_error_ = result; - } else { - bytes_read = result; - } - DCHECK_GE(recv_buffer_->RemainingCapacity(), bytes_read); - int ret = BIO_zero_copy_get_write_buf_done(transport_bio_.get(), bytes_read); - DCHECK_EQ(1, ret); - transport_recv_busy_ = false; - return result; -} - int SSLClientSocketImpl::VerifyCT() { const uint8_t* sct_list_raw; size_t sct_list_len; @@ -1912,53 +1700,6 @@ return 1; } -long SSLClientSocketImpl::MaybeReplayTransportError(BIO* bio, - int cmd, - const char* argp, - int argi, - long argl, - long retvalue) { - if (cmd == (BIO_CB_READ | BIO_CB_RETURN) && retvalue <= 0) { - // If there is no more data in the buffer, report any pending errors that - // were observed. Note that both the readbuf and the writebuf are checked - // for errors, since the application may have encountered a socket error - // while writing that would otherwise not be reported until the application - // attempted to write again - which it may never do. See - // https://crbug.com/249848. - if (transport_read_error_ != OK) { - OpenSSLPutNetError(FROM_HERE, transport_read_error_); - return -1; - } - if (transport_write_error_ != OK) { - OpenSSLPutNetError(FROM_HERE, transport_write_error_); - return -1; - } - } else if (cmd == BIO_CB_WRITE) { - // Because of the write buffer, this reports a failure from the previous - // write payload. If the current payload fails to write, the error will be - // reported in a future write or read to |bio|. - if (transport_write_error_ != OK) { - OpenSSLPutNetError(FROM_HERE, transport_write_error_); - return -1; - } - } - return retvalue; -} - -// static -long SSLClientSocketImpl::BIOCallback(BIO* bio, - int cmd, - const char* argp, - int argi, - long argl, - long retvalue) { - SSLClientSocketImpl* socket = - reinterpret_cast<SSLClientSocketImpl*>(BIO_get_callback_arg(bio)); - CHECK(socket); - return socket->MaybeReplayTransportError(bio, cmd, argp, argi, argl, - retvalue); -} - void SSLClientSocketImpl::MaybeCacheSession() { // Only cache the session once both a new session has been established and the // certificate has been verified. Due to False Start, these events may happen @@ -2097,14 +1838,9 @@ if (signature_result_ == OK) signature_ = signature; - if (next_handshake_state_ == STATE_HANDSHAKE) { - OnHandshakeIOComplete(signature_result_); - return; - } - // During a renegotiation, either Read or Write calls may be blocked on an // asynchronous private key operation. - PumpReadWriteEvents(); + RetryAllOperations(); } int SSLClientSocketImpl::TokenBindingAdd(const uint8_t** out,
diff --git a/net/socket/ssl_client_socket_impl.h b/net/socket/ssl_client_socket_impl.h index ff235ed..010a6f00 100644 --- a/net/socket/ssl_client_socket_impl.h +++ b/net/socket/ssl_client_socket_impl.h
@@ -26,6 +26,7 @@ #include "net/cert/ct_verify_result.h" #include "net/log/net_log_with_source.h" #include "net/socket/client_socket_handle.h" +#include "net/socket/socket_bio_adapter.h" #include "net/socket/ssl_client_socket.h" #include "net/ssl/channel_id_service.h" #include "net/ssl/openssl_ssl_util.h" @@ -45,6 +46,7 @@ class CertVerifier; class CTVerifier; +class SocketBIOAdapter; class SSLCertRequestInfo; class SSLInfo; @@ -52,7 +54,8 @@ base::MRUCache<std::pair<TokenBindingType, std::string>, std::vector<uint8_t>>; -class SSLClientSocketImpl : public SSLClientSocket { +class SSLClientSocketImpl : public SSLClientSocket, + public SocketBIOAdapter::Delegate { public: // Takes ownership of the transport_socket, which may already be connected. // The given hostname will be compared with the name(s) in the server's @@ -121,6 +124,10 @@ int SetReceiveBufferSize(int32_t size) override; int SetSendBufferSize(int32_t size) override; + // SocketBIOAdapter implementation: + void OnReadReady() override; + void OnWriteReady() override; + private: class PeerCertificateChain; class SSLContext; @@ -131,7 +138,6 @@ void DoReadCallback(int result); void DoWriteCallback(int result); - bool DoTransportIO(); int DoHandshake(); int DoHandshakeComplete(int result); int DoChannelIDLookup(); @@ -142,26 +148,16 @@ void UpdateServerCert(); void OnHandshakeIOComplete(int result); - void OnSendComplete(int result); - void OnRecvComplete(int result); int DoHandshakeLoop(int last_io_result); - int DoReadLoop(); - int DoWriteLoop(); int DoPayloadRead(); int DoPayloadWrite(); // Called when an asynchronous event completes which may have blocked the - // pending Read or Write calls, if any. Retries both state machines and, if - // complete, runs the respective callbacks. - void PumpReadWriteEvents(); + // pending Connect, Read or Write calls, if any. Retries all state machines + // and, if complete, runs the respective callbacks. + void RetryAllOperations(); - int BufferSend(); - int BufferRecv(); - void BufferSendComplete(int result); - void BufferRecvComplete(int result); - void TransportWriteComplete(int result); - int TransportReadComplete(int result); int VerifyCT(); // Callback from the SSL layer that indicates the remote server is requesting @@ -173,25 +169,6 @@ // certificates don't change during renegotiation. int CertVerifyCallback(X509_STORE_CTX* store_ctx); - // Called during an operation on |transport_bio_|'s peer. Checks saved - // transport error state and, if appropriate, returns an error through - // OpenSSL's error system. - long MaybeReplayTransportError(BIO* bio, - int cmd, - const char* argp, - int argi, - long argl, - long retvalue); - - // Callback from the SSL layer when an operation is performed on - // |transport_bio_|'s peer. - static long BIOCallback(BIO* bio, - int cmd, - const char* argp, - int argi, - long argl, - long retvalue); - // Called after the initial handshake completes and after the server // certificate has been verified. The order of handshake completion and // certificate verification depends on whether the connection was false @@ -258,14 +235,6 @@ const crypto::OpenSSLErrStackTracer& tracer, OpenSSLErrorInfo* info); - bool transport_send_busy_; - bool transport_recv_busy_; - - // Buffers which are shared by BoringSSL and SSLClientSocketImpl. - // GrowableIOBuffer is used to keep ownership and setting offset. - scoped_refptr<GrowableIOBuffer> send_buffer_; - scoped_refptr<GrowableIOBuffer> recv_buffer_; - CompletionCallback user_connect_callback_; CompletionCallback user_read_callback_; CompletionCallback user_write_callback_; @@ -293,15 +262,6 @@ // If there is a pending read result, the OpenSSLErrorInfo associated with it. OpenSSLErrorInfo pending_read_error_info_; - // Used by TransportReadComplete() to signify an error reading from the - // transport socket. A value of OK indicates the socket is still - // readable. EOFs are mapped to ERR_CONNECTION_CLOSED. - int transport_read_error_; - - // Used by TransportWriteComplete() and TransportReadComplete() to signify an - // error writing to the transport socket. A value of OK indicates no error. - int transport_write_error_; - // Set when Connect finishes. std::unique_ptr<PeerCertificateChain> server_cert_chain_; scoped_refptr<X509Certificate> server_cert_; @@ -336,9 +296,9 @@ // OpenSSL stuff bssl::UniquePtr<SSL> ssl_; - bssl::UniquePtr<BIO> transport_bio_; std::unique_ptr<ClientSocketHandle> transport_; + std::unique_ptr<SocketBIOAdapter> transport_adapter_; const HostPortPair host_and_port_; SSLConfig ssl_config_; // ssl_session_cache_shard_ is an opaque string that partitions the SSL
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc index a0ac613..a613dd9 100644 --- a/net/socket/ssl_client_socket_unittest.cc +++ b/net/socket/ssl_client_socket_unittest.cc
@@ -1255,7 +1255,7 @@ } // Tests that SSLClientSocket properly handles when the underlying transport -// synchronously fails a transport read in during the handshake. +// synchronously fails a transport write in during the handshake. TEST_F(SSLClientSocketTest, Connect_WithSynchronousError) { ASSERT_TRUE(StartTestServer(SpawnedTestServer::SSLOptions())); @@ -1643,32 +1643,29 @@ raw_error_socket->SetNextWriteError(ERR_CONNECTION_RESET); - // Write as much data as possible until hitting an error. This is necessary - // for NSS. PR_Write will only consume as much data as it can encode into - // application data records before the internal memio buffer is full, which - // should only fill if writing a large amount of data and the underlying - // transport is blocked. Once this happens, NSS will return (total size of all - // application data records it wrote) - 1, with the caller expected to resume - // with the remaining unsent data. + // Write as much data as possible until hitting an error. do { rv = callback.GetResult(sock->Write(long_request_buffer.get(), long_request_buffer->BytesRemaining(), callback.callback())); if (rv > 0) { long_request_buffer->DidConsume(rv); - // Abort if the entire buffer is ever consumed. + // Abort if the entire input is ever consumed. The input is larger than + // the SSLClientSocket's write buffers. ASSERT_LT(0, long_request_buffer->BytesRemaining()); } } while (rv > 0); EXPECT_THAT(rv, IsError(ERR_CONNECTION_RESET)); - // Release the read. - raw_transport->UnblockReadResult(); + // At this point the Read result is available. Transport write errors are + // surfaced through Writes. See https://crbug.com/249848. rv = read_callback.WaitForResult(); + EXPECT_THAT(rv, IsError(ERR_CONNECTION_RESET)); - // Should still read bytes despite the write error. - EXPECT_LT(0, rv); + // Release the read. This does not cause a crash. + raw_transport->UnblockReadResult(); + base::RunLoop().RunUntilIdle(); } // Tests that SSLClientSocket fails the handshake if the underlying
diff --git a/net/socket/ssl_server_socket_impl.cc b/net/socket/ssl_server_socket_impl.cc index 9eadcdb..c7d1e667 100644 --- a/net/socket/ssl_server_socket_impl.cc +++ b/net/socket/ssl_server_socket_impl.cc
@@ -20,6 +20,7 @@ #include "net/cert/x509_util_openssl.h" #include "net/log/net_log_event_type.h" #include "net/log/net_log_with_source.h" +#include "net/socket/socket_bio_adapter.h" #include "net/ssl/openssl_ssl_util.h" #include "net/ssl/ssl_connection_status_flags.h" #include "net/ssl/ssl_info.h" @@ -53,7 +54,8 @@ return X509Certificate::CreateFromDERCertChain(der_chain); } -class SSLServerSocketImpl : public SSLServerSocket { +class SSLServerSocketImpl : public SSLServerSocket, + public SocketBIOAdapter::Delegate { public: // See comments on CreateSSLServerSocket for details of how these // parameters are used. @@ -101,29 +103,22 @@ int64_t GetTotalReceivedBytes() const override; static int CertVerifyCallback(X509_STORE_CTX* store_ctx, void* arg); + // SocketBIOAdapter::Delegate implementation. + void OnReadReady() override; + void OnWriteReady() override; + private: enum State { STATE_NONE, STATE_HANDSHAKE, }; - void OnSendComplete(int result); - void OnRecvComplete(int result); void OnHandshakeIOComplete(int result); - int BufferSend(); - void BufferSendComplete(int result); - void TransportWriteComplete(int result); - int BufferRecv(); - void BufferRecvComplete(int result); - int TransportReadComplete(int result); - bool DoTransportIO(); int DoPayloadRead(); int DoPayloadWrite(); int DoHandshakeLoop(int last_io_result); - int DoReadLoop(int result); - int DoWriteLoop(int result); int DoHandshake(); void DoHandshakeCallback(int result); void DoReadCallback(int result); @@ -132,14 +127,6 @@ int Init(); void ExtractClientCert(); - // Members used to send and receive buffer. - bool transport_send_busy_; - bool transport_recv_busy_; - bool transport_recv_eof_; - - scoped_refptr<DrainableIOBuffer> send_buffer_; - scoped_refptr<IOBuffer> recv_buffer_; - NetLogWithSource net_log_; CompletionCallback user_handshake_callback_; @@ -154,16 +141,12 @@ scoped_refptr<IOBuffer> user_write_buf_; int user_write_buf_len_; - // Used by TransportWriteComplete() and TransportReadComplete() to signify an - // error writing to the transport socket. A value of OK indicates no error. - int transport_write_error_; - // OpenSSL stuff bssl::UniquePtr<SSL> ssl_; - bssl::UniquePtr<BIO> transport_bio_; // StreamSocket for sending and receiving data. std::unique_ptr<StreamSocket> transport_socket_; + std::unique_ptr<SocketBIOAdapter> transport_adapter_; // Certificate for the client. scoped_refptr<X509Certificate> client_cert_; @@ -177,12 +160,8 @@ SSLServerSocketImpl::SSLServerSocketImpl( std::unique_ptr<StreamSocket> transport_socket, bssl::UniquePtr<SSL> ssl) - : transport_send_busy_(false), - transport_recv_busy_(false), - transport_recv_eof_(false), - user_read_buf_len_(0), + : user_read_buf_len_(0), user_write_buf_len_(0), - transport_write_error_(OK), ssl_(std::move(ssl)), transport_socket_(std::move(transport_socket)), next_handshake_state_(STATE_NONE), @@ -261,7 +240,7 @@ DCHECK(completed_handshake_); - int rv = DoReadLoop(OK); + int rv = DoPayloadRead(); if (rv == ERR_IO_PENDING) { user_read_callback_ = callback; @@ -283,7 +262,7 @@ user_write_buf_ = buf; user_write_buf_len_ = buf_len; - int rv = DoWriteLoop(OK); + int rv = DoPayloadWrite(); if (rv == ERR_IO_PENDING) { user_write_callback_ = callback; @@ -394,45 +373,40 @@ return transport_socket_->GetTotalReceivedBytes(); } -void SSLServerSocketImpl::OnSendComplete(int result) { +void SSLServerSocketImpl::OnReadReady() { if (next_handshake_state_ == STATE_HANDSHAKE) { - // In handshake phase. - OnHandshakeIOComplete(result); + // In handshake phase. The parameter to OnHandshakeIOComplete is unused. + OnHandshakeIOComplete(OK); return; } - // TODO(byungchul): This state machine is not correct. Copy the state machine - // of SSLClientSocketImpl::OnSendComplete() which handles it better. - if (!completed_handshake_) + // BoringSSL does not support renegotiation as a server, so the only other + // operation blocked on Read is DoPayloadRead. + if (!user_read_buf_) return; - if (user_write_buf_) { - int rv = DoWriteLoop(result); - if (rv != ERR_IO_PENDING) - DoWriteCallback(rv); - } else { - // Ensure that any queued ciphertext is flushed. - DoTransportIO(); - } -} - -void SSLServerSocketImpl::OnRecvComplete(int result) { - if (next_handshake_state_ == STATE_HANDSHAKE) { - // In handshake phase. - OnHandshakeIOComplete(result); - return; - } - - // Network layer received some data, check if client requested to read - // decrypted data. - if (!user_read_buf_ || !completed_handshake_) - return; - - int rv = DoReadLoop(result); + int rv = DoPayloadRead(); if (rv != ERR_IO_PENDING) DoReadCallback(rv); } +void SSLServerSocketImpl::OnWriteReady() { + if (next_handshake_state_ == STATE_HANDSHAKE) { + // In handshake phase. The parameter to OnHandshakeIOComplete is unused. + OnHandshakeIOComplete(OK); + return; + } + + // BoringSSL does not support renegotiation as a server, so the only other + // operation blocked on Read is DoPayloadWrite. + if (!user_write_buf_) + return; + + int rv = DoPayloadWrite(); + if (rv != ERR_IO_PENDING) + DoWriteCallback(rv); +} + void SSLServerSocketImpl::OnHandshakeIOComplete(int result) { int rv = DoHandshakeLoop(result); if (rv == ERR_IO_PENDING) @@ -443,161 +417,13 @@ DoHandshakeCallback(rv); } -// Return 0 for EOF, -// > 0 for bytes transferred immediately, -// < 0 for error (or the non-error ERR_IO_PENDING). -int SSLServerSocketImpl::BufferSend() { - if (transport_send_busy_) - return ERR_IO_PENDING; - - if (!send_buffer_) { - // Get a fresh send buffer out of the send BIO. - size_t max_read = BIO_pending(transport_bio_.get()); - if (!max_read) - return 0; // Nothing pending in the OpenSSL write BIO. - send_buffer_ = new DrainableIOBuffer(new IOBuffer(max_read), max_read); - int read_bytes = - BIO_read(transport_bio_.get(), send_buffer_->data(), max_read); - DCHECK_GT(read_bytes, 0); - CHECK_EQ(static_cast<int>(max_read), read_bytes); - } - - int rv = transport_socket_->Write( - send_buffer_.get(), send_buffer_->BytesRemaining(), - base::Bind(&SSLServerSocketImpl::BufferSendComplete, - base::Unretained(this))); - if (rv == ERR_IO_PENDING) { - transport_send_busy_ = true; - } else { - TransportWriteComplete(rv); - } - return rv; -} - -void SSLServerSocketImpl::BufferSendComplete(int result) { - transport_send_busy_ = false; - TransportWriteComplete(result); - OnSendComplete(result); -} - -void SSLServerSocketImpl::TransportWriteComplete(int result) { - DCHECK(ERR_IO_PENDING != result); - if (result < 0) { - // Got a socket write error; close the BIO to indicate this upward. - // - // TODO(davidben): The value of |result| gets lost. Feed the error back into - // the BIO so it gets (re-)detected in OnSendComplete. Perhaps with - // BIO_set_callback. - DVLOG(1) << "TransportWriteComplete error " << result; - (void)BIO_shutdown_wr(SSL_get_wbio(ssl_.get())); - - // Match the fix for http://crbug.com/249848 in NSS by erroring future reads - // from the socket after a write error. - // - // TODO(davidben): Avoid having read and write ends interact this way. - transport_write_error_ = result; - (void)BIO_shutdown_wr(transport_bio_.get()); - send_buffer_ = NULL; - } else { - DCHECK(send_buffer_); - send_buffer_->DidConsume(result); - DCHECK_GE(send_buffer_->BytesRemaining(), 0); - if (send_buffer_->BytesRemaining() <= 0) - send_buffer_ = NULL; - } -} - -int SSLServerSocketImpl::BufferRecv() { - if (transport_recv_busy_) - return ERR_IO_PENDING; - - // Determine how much was requested from |transport_bio_| that was not - // actually available. - size_t requested = BIO_ctrl_get_read_request(transport_bio_.get()); - if (requested == 0) { - // This is not a perfect match of error codes, as no operation is - // actually pending. However, returning 0 would be interpreted as - // a possible sign of EOF, which is also an inappropriate match. - return ERR_IO_PENDING; - } - - // Known Issue: While only reading |requested| data is the more correct - // implementation, it has the downside of resulting in frequent reads: - // One read for the SSL record header (~5 bytes) and one read for the SSL - // record body. Rather than issuing these reads to the underlying socket - // (and constantly allocating new IOBuffers), a single Read() request to - // fill |transport_bio_| is issued. As long as an SSL client socket cannot - // be gracefully shutdown (via SSL close alerts) and re-used for non-SSL - // traffic, this over-subscribed Read()ing will not cause issues. - size_t max_write = BIO_ctrl_get_write_guarantee(transport_bio_.get()); - if (!max_write) - return ERR_IO_PENDING; - - recv_buffer_ = new IOBuffer(max_write); - int rv = transport_socket_->Read( - recv_buffer_.get(), max_write, - base::Bind(&SSLServerSocketImpl::BufferRecvComplete, - base::Unretained(this))); - if (rv == ERR_IO_PENDING) { - transport_recv_busy_ = true; - } else { - rv = TransportReadComplete(rv); - } - return rv; -} - -void SSLServerSocketImpl::BufferRecvComplete(int result) { - result = TransportReadComplete(result); - OnRecvComplete(result); -} - -int SSLServerSocketImpl::TransportReadComplete(int result) { - DCHECK(ERR_IO_PENDING != result); - if (result <= 0) { - DVLOG(1) << "TransportReadComplete result " << result; - // Received 0 (end of file) or an error. Either way, bubble it up to the - // SSL layer via the BIO. TODO(joth): consider stashing the error code, to - // relay up to the SSL socket client (i.e. via DoReadCallback). - if (result == 0) - transport_recv_eof_ = true; - (void)BIO_shutdown_wr(transport_bio_.get()); - } else if (transport_write_error_ < 0) { - // Mirror transport write errors as read failures; transport_bio_ has been - // shut down by TransportWriteComplete, so the BIO_write will fail, failing - // the CHECK. http://crbug.com/335557. - result = transport_write_error_; - } else { - DCHECK(recv_buffer_); - int ret = BIO_write(transport_bio_.get(), recv_buffer_->data(), result); - // A write into a memory BIO should always succeed. - DCHECK_EQ(result, ret); - } - recv_buffer_ = NULL; - transport_recv_busy_ = false; - return result; -} - -// Do as much network I/O as possible between the buffer and the -// transport socket. Return true if some I/O performed, false -// otherwise (error or ERR_IO_PENDING). -bool SSLServerSocketImpl::DoTransportIO() { - bool network_moved = false; - int rv; - // Read and write as much data as possible. The loop is necessary because - // Write() may return synchronously. - do { - rv = BufferSend(); - if (rv != ERR_IO_PENDING && rv != 0) - network_moved = true; - } while (rv > 0); - if (!transport_recv_eof_ && BufferRecv() != ERR_IO_PENDING) - network_moved = true; - return network_moved; -} int SSLServerSocketImpl::DoPayloadRead() { + DCHECK(completed_handshake_); + DCHECK_EQ(STATE_NONE, next_handshake_state_); DCHECK(user_read_buf_); DCHECK_GT(user_read_buf_len_, 0); + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); int rv = SSL_read(ssl_.get(), user_read_buf_->data(), user_read_buf_len_); if (rv >= 0) @@ -615,7 +441,10 @@ } int SSLServerSocketImpl::DoPayloadWrite() { + DCHECK(completed_handshake_); + DCHECK_EQ(STATE_NONE, next_handshake_state_); DCHECK(user_write_buf_); + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); int rv = SSL_write(ssl_.get(), user_write_buf_->data(), user_write_buf_len_); if (rv >= 0) @@ -652,51 +481,10 @@ LOG(DFATAL) << "unexpected state " << state; break; } - - // Do the actual network I/O - bool network_moved = DoTransportIO(); - if (network_moved && next_handshake_state_ == STATE_HANDSHAKE) { - // In general we exit the loop if rv is ERR_IO_PENDING. In this - // special case we keep looping even if rv is ERR_IO_PENDING because - // the transport IO may allow DoHandshake to make progress. - rv = OK; // This causes us to stay in the loop. - } } while (rv != ERR_IO_PENDING && next_handshake_state_ != STATE_NONE); return rv; } -int SSLServerSocketImpl::DoReadLoop(int result) { - DCHECK(completed_handshake_); - DCHECK(next_handshake_state_ == STATE_NONE); - - if (result < 0) - return result; - - bool network_moved; - int rv; - do { - rv = DoPayloadRead(); - network_moved = DoTransportIO(); - } while (rv == ERR_IO_PENDING && network_moved); - return rv; -} - -int SSLServerSocketImpl::DoWriteLoop(int result) { - DCHECK(completed_handshake_); - DCHECK_EQ(next_handshake_state_, STATE_NONE); - - if (result < 0) - return result; - - bool network_moved; - int rv; - do { - rv = DoPayloadWrite(); - network_moved = DoTransportIO(); - } while (rv == ERR_IO_PENDING && network_moved); - return rv; -} - int SSLServerSocketImpl::DoHandshake() { crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); int net_error = OK; @@ -764,23 +552,22 @@ } int SSLServerSocketImpl::Init() { - DCHECK(!transport_bio_); + static const int kBufferSize = 17 * 1024; crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); if (!ssl_) return ERR_UNEXPECTED; - BIO* ssl_bio = nullptr; - BIO* transport_bio_raw = nullptr; - // 0 => use default buffer sizes. - if (!BIO_new_bio_pair(&ssl_bio, 0, &transport_bio_raw, 0)) - return ERR_UNEXPECTED; - transport_bio_.reset(transport_bio_raw); - DCHECK(ssl_bio); - DCHECK(transport_bio_); + transport_adapter_.reset(new SocketBIOAdapter( + transport_socket_.get(), kBufferSize, kBufferSize, this)); + BIO* transport_bio = transport_adapter_->bio(); - SSL_set_bio(ssl_.get(), ssl_bio, ssl_bio); + BIO_up_ref(transport_bio); // SSL_set0_rbio takes ownership. + SSL_set0_rbio(ssl_.get(), transport_bio); + + BIO_up_ref(transport_bio); // SSL_set0_wbio takes ownership. + SSL_set0_wbio(ssl_.get(), transport_bio); return OK; }
diff --git a/net/socket/ssl_server_socket_unittest.cc b/net/socket/ssl_server_socket_unittest.cc index 8f22947..e6a71a1 100644 --- a/net/socket/ssl_server_socket_unittest.cc +++ b/net/socket/ssl_server_socket_unittest.cc
@@ -787,7 +787,8 @@ client_socket_->Disconnect(); - EXPECT_THAT(handshake_callback.GetResult(server_ret), IsError(ERR_FAILED)); + EXPECT_THAT(handshake_callback.GetResult(server_ret), + IsError(ERR_CONNECTION_CLOSED)); } TEST_F(SSLServerSocketTest, HandshakeWithClientCertRequiredNotSuppliedCached) { @@ -820,7 +821,8 @@ client_socket_->Disconnect(); - EXPECT_THAT(handshake_callback.GetResult(server_ret), IsError(ERR_FAILED)); + EXPECT_THAT(handshake_callback.GetResult(server_ret), + IsError(ERR_CONNECTION_CLOSED)); server_socket_->Disconnect(); // Below, check that the cache didn't store the result of a failed handshake. @@ -842,7 +844,8 @@ client_socket_->Disconnect(); - EXPECT_THAT(handshake_callback2.GetResult(server_ret2), IsError(ERR_FAILED)); + EXPECT_THAT(handshake_callback2.GetResult(server_ret2), + IsError(ERR_CONNECTION_CLOSED)); } TEST_F(SSLServerSocketTest, HandshakeWithWrongClientCertSupplied) {
diff --git a/net/ssl/openssl_ssl_util.h b/net/ssl/openssl_ssl_util.h index 06fd4090..04bcf30 100644 --- a/net/ssl/openssl_ssl_util.h +++ b/net/ssl/openssl_ssl_util.h
@@ -9,6 +9,7 @@ #include <openssl/x509.h> +#include "net/base/net_export.h" #include "net/cert/x509_certificate.h" #include "net/log/net_log_parameters_callback.h" @@ -42,7 +43,9 @@ // Note that |tracer| is not currently used in the implementation, but is passed // in anyway as this ensures the caller will clear any residual codes left on // the error stack. -int MapOpenSSLError(int err, const crypto::OpenSSLErrStackTracer& tracer); +NET_EXPORT_PRIVATE int MapOpenSSLError( + int err, + const crypto::OpenSSLErrStackTracer& tracer); // Helper struct to store information about an OpenSSL error stack entry. struct OpenSSLErrorInfo {
diff --git a/remoting/client/plugin/pepper_video_renderer_3d.cc b/remoting/client/plugin/pepper_video_renderer_3d.cc index b60f9bd7..aff694a2 100644 --- a/remoting/client/plugin/pepper_video_renderer_3d.cc +++ b/remoting/client/plugin/pepper_video_renderer_3d.cc
@@ -91,14 +91,22 @@ DCHECK(event_handler); DCHECK(!event_handler_); + fallback_renderer_.SetPepperContext(instance, event_handler); + event_handler_ = event_handler; pp_instance_ = instance; } void PepperVideoRenderer3D::OnViewChanged(const pp::View& view) { + fallback_renderer_.OnViewChanged(view); + pp::Size size = view.GetRect().size(); float scale = view.GetDeviceScale(); - view_size_.set(ceilf(size.width() * scale), ceilf(size.height() * scale)); + DCHECK_GT(scale, 0.0); + view_size_.set(std::min<int>(ceilf(size.width() * scale), + gl_max_viewport_size_[0]), + std::min<int>(ceilf(size.height() * scale), + gl_max_viewport_size_[1])); graphics_.ResizeBuffers(view_size_.width(), view_size_.height()); force_repaint_ = true; @@ -106,12 +114,17 @@ } void PepperVideoRenderer3D::EnableDebugDirtyRegion(bool enable) { + fallback_renderer_.EnableDebugDirtyRegion(enable); debug_dirty_region_ = enable; } bool PepperVideoRenderer3D::Initialize( const ClientContext& context, protocol::FrameStatsConsumer* stats_consumer) { + if (!fallback_renderer_.Initialize(context, stats_consumer)) { + LOG(FATAL) << "Failed to initialize fallback_renderer_"; + } + stats_consumer_ = stats_consumer; const int32_t context_attributes[] = { @@ -167,6 +180,11 @@ gles2_if_->BufferData(graphics_3d, GL_ARRAY_BUFFER, sizeof(kVertices), kVertices, GL_STATIC_DRAW); + gles2_if_->GetIntegerv( + graphics_3d, GL_MAX_TEXTURE_SIZE, &gl_max_texture_size_); + gles2_if_->GetIntegerv( + graphics_3d, GL_MAX_VIEWPORT_DIMS, gl_max_viewport_size_); + CheckGLError(); return true; @@ -174,6 +192,8 @@ void PepperVideoRenderer3D::OnSessionConfig( const protocol::SessionConfig& config) { + fallback_renderer_.OnSessionConfig(config); + PP_VideoProfile video_profile = PP_VIDEOPROFILE_VP8_ANY; switch (config.video_config().codec) { case protocol::ChannelConfig::CODEC_VP8: @@ -212,6 +232,29 @@ void PepperVideoRenderer3D::ProcessVideoPacket( std::unique_ptr<VideoPacket> packet, const base::Closure& done) { + if (!use_fallback_renderer_ && + packet->format().has_screen_width() && + packet->format().has_screen_height() && + (packet->format().screen_width() > gl_max_texture_size_ || + packet->format().screen_height() > gl_max_texture_size_)) { + use_fallback_renderer_ = true; + // Clear current instance and use fallback_renderer_. + current_picture_frames_.clear(); + current_picture_.reset(); + next_picture_frames_.clear(); + next_picture_.reset(); + decoded_frames_.clear(); + pending_frames_.clear(); + graphics_ = pp::Graphics3D(); + video_decoder_ = pp::VideoDecoder(); + } + + if (use_fallback_renderer_) { + fallback_renderer_.GetVideoStub()->ProcessVideoPacket( + std::move(packet), done); + return; + } + VideoPacket* packet_ptr = packet.get(); std::unique_ptr<FrameTracker> frame_tracker( new FrameTracker(std::move(packet), stats_consumer_, done));
diff --git a/remoting/client/plugin/pepper_video_renderer_3d.h b/remoting/client/plugin/pepper_video_renderer_3d.h index e1699ecf..23494640 100644 --- a/remoting/client/plugin/pepper_video_renderer_3d.h +++ b/remoting/client/plugin/pepper_video_renderer_3d.h
@@ -18,6 +18,7 @@ #include "ppapi/cpp/video_decoder.h" #include "ppapi/utility/completion_callback_factory.h" #include "remoting/client/plugin/pepper_video_renderer.h" +#include "remoting/client/plugin/pepper_video_renderer_2d.h" #include "remoting/protocol/video_stub.h" #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" @@ -136,6 +137,11 @@ // the |current_picture_| is rendered. std::list<std::unique_ptr<FrameTracker>> current_picture_frames_; + // The fallback software renderer, if input video packet size is larger than + // hardware limitation. + PepperVideoRenderer2D fallback_renderer_; + bool use_fallback_renderer_ = false; + // Set to true if the screen has been resized and needs to be repainted. bool force_repaint_ = false; @@ -158,6 +164,10 @@ pp::CompletionCallbackFactory<PepperVideoRenderer3D> callback_factory_; + // The hardware limitation. + int gl_max_texture_size_; + int gl_max_viewport_size_[2]; + DISALLOW_COPY_AND_ASSIGN(PepperVideoRenderer3D); };
diff --git a/remoting/protocol/BUILD.gn b/remoting/protocol/BUILD.gn index 4d399ef..3c839d91 100644 --- a/remoting/protocol/BUILD.gn +++ b/remoting/protocol/BUILD.gn
@@ -326,6 +326,7 @@ "validating_authenticator_unittest.cc", "video_frame_pump_unittest.cc", "webrtc_audio_source_adapter_unittest.cc", + "webrtc_frame_scheduler_unittest.cc", "webrtc_transport_unittest.cc", ]
diff --git a/remoting/protocol/connection_unittest.cc b/remoting/protocol/connection_unittest.cc index 8c81430..fc063183 100644 --- a/remoting/protocol/connection_unittest.cc +++ b/remoting/protocol/connection_unittest.cc
@@ -534,11 +534,12 @@ Connect(); base::TimeTicks start_time = base::TimeTicks::Now(); + base::TimeTicks event_timestamp = base::TimeTicks::FromInternalValue(42); scoped_refptr<InputEventTimestampsSourceImpl> input_event_timestamps_source = new InputEventTimestampsSourceImpl(); input_event_timestamps_source->OnEventReceived( - InputEventTimestamps{start_time, start_time}); + InputEventTimestamps{event_timestamp, start_time}); std::unique_ptr<VideoStream> video_stream = host_connection_->StartVideoStream( @@ -554,30 +555,27 @@ const FrameStats& stats = client_video_renderer_.GetFrameStatsConsumer()->received_stats().front(); - EXPECT_TRUE(stats.host_stats.frame_size > 0); + EXPECT_GT(stats.host_stats.frame_size, 0); - EXPECT_TRUE(stats.host_stats.latest_event_timestamp == start_time); - EXPECT_TRUE(stats.host_stats.capture_delay != base::TimeDelta::Max()); - EXPECT_TRUE(stats.host_stats.capture_overhead_delay != - base::TimeDelta::Max()); - EXPECT_TRUE(stats.host_stats.encode_delay != base::TimeDelta::Max()); - EXPECT_TRUE(stats.host_stats.send_pending_delay != base::TimeDelta::Max()); + EXPECT_EQ(stats.host_stats.latest_event_timestamp, event_timestamp); + EXPECT_NE(stats.host_stats.capture_delay, base::TimeDelta::Max()); + EXPECT_NE(stats.host_stats.capture_overhead_delay, base::TimeDelta::Max()); + EXPECT_NE(stats.host_stats.encode_delay, base::TimeDelta::Max()); + EXPECT_NE(stats.host_stats.send_pending_delay, base::TimeDelta::Max()); EXPECT_FALSE(stats.client_stats.time_received.is_null()); EXPECT_FALSE(stats.client_stats.time_decoded.is_null()); EXPECT_FALSE(stats.client_stats.time_rendered.is_null()); - EXPECT_TRUE(start_time + stats.host_stats.capture_pending_delay + - stats.host_stats.capture_delay + - stats.host_stats.capture_overhead_delay + - stats.host_stats.encode_delay + - stats.host_stats.send_pending_delay <= - stats.client_stats.time_received); - EXPECT_TRUE(stats.client_stats.time_received <= - stats.client_stats.time_decoded); - EXPECT_TRUE(stats.client_stats.time_decoded <= - stats.client_stats.time_rendered); - EXPECT_TRUE(stats.client_stats.time_rendered <= finish_time); + EXPECT_LE(start_time + stats.host_stats.capture_pending_delay + + stats.host_stats.capture_delay + + stats.host_stats.capture_overhead_delay + + stats.host_stats.encode_delay + + stats.host_stats.send_pending_delay, + stats.client_stats.time_received); + EXPECT_LE(stats.client_stats.time_received, stats.client_stats.time_decoded); + EXPECT_LE(stats.client_stats.time_decoded, stats.client_stats.time_rendered); + EXPECT_LE(stats.client_stats.time_rendered, finish_time); } TEST_P(ConnectionTest, Audio) {
diff --git a/remoting/protocol/webrtc_dummy_video_encoder.h b/remoting/protocol/webrtc_dummy_video_encoder.h index 4f59c14..92b63994 100644 --- a/remoting/protocol/webrtc_dummy_video_encoder.h +++ b/remoting/protocol/webrtc_dummy_video_encoder.h
@@ -88,6 +88,10 @@ void SetVideoChannelStateObserver( base::WeakPtr<VideoChannelStateObserver> video_channel_state_observer); + base::WeakPtr<VideoChannelStateObserver> + get_video_channel_state_observer_for_tests() { + return video_channel_state_observer_; + } private: scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
diff --git a/remoting/protocol/webrtc_frame_scheduler_simple.cc b/remoting/protocol/webrtc_frame_scheduler_simple.cc index 336276d..14d8724 100644 --- a/remoting/protocol/webrtc_frame_scheduler_simple.cc +++ b/remoting/protocol/webrtc_frame_scheduler_simple.cc
@@ -64,8 +64,7 @@ void WebrtcFrameSchedulerSimple::OnKeyFrameRequested() { DCHECK(thread_checker_.CalledOnValidThread()); key_frame_request_ = true; - if (!capture_timer_.IsRunning()) - ScheduleNextFrame(base::TimeTicks::Now()); + ScheduleNextFrame(base::TimeTicks::Now()); } void WebrtcFrameSchedulerSimple::OnChannelParameters(int packet_loss, @@ -109,6 +108,7 @@ if (frame.updated_region().is_empty() && !top_off_is_active_ && !key_frame_request_) { + frame_pending_ = false; ScheduleNextFrame(now); return false; } @@ -157,6 +157,8 @@ const WebrtcVideoEncoder::EncodedFrame& encoded_frame, const webrtc::EncodedImageCallback::Result& send_result) { DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(frame_pending_); + frame_pending_ = false; base::TimeTicks now = base::TimeTicks::Now(); pacing_bucket_.RefillOrSpill(encoded_frame.data.size(), now); @@ -177,10 +179,10 @@ void WebrtcFrameSchedulerSimple::ScheduleNextFrame(base::TimeTicks now) { DCHECK(thread_checker_.CalledOnValidThread()); - // Don't capture frames when paused or target bitrate is 0 or there is - // no capture callback set. - if (paused_ || pacing_bucket_.rate() == 0 || capture_callback_.is_null()) + if (paused_ || pacing_bucket_.rate() == 0 || capture_callback_.is_null() || + frame_pending_) { return; + } // If this is not the first frame then capture next frame after the previous // one has finished sending. @@ -205,8 +207,9 @@ void WebrtcFrameSchedulerSimple::CaptureNextFrame() { DCHECK(thread_checker_.CalledOnValidThread()); - + DCHECK(!frame_pending_); last_capture_started_time_ = base::TimeTicks::Now(); + frame_pending_ = true; capture_callback_.Run(); }
diff --git a/remoting/protocol/webrtc_frame_scheduler_simple.h b/remoting/protocol/webrtc_frame_scheduler_simple.h index 6a38b39d..f5f3702 100644 --- a/remoting/protocol/webrtc_frame_scheduler_simple.h +++ b/remoting/protocol/webrtc_frame_scheduler_simple.h
@@ -16,9 +16,10 @@ namespace remoting { namespace protocol { -// WebrtcFrameSchedulerSimple is a simple implementation of -// WebrtcFrameScheduler that always keeps only one frame in the pipeline. -// It schedules each frame after the previous one is expected to finish sending. +// WebrtcFrameSchedulerSimple is a simple implementation of WebrtcFrameScheduler +// that always keeps only one frame in the pipeline. It schedules each frame +// such that it is encoded and ready to be sent by the time previous one is +// expected to finish sending. class WebrtcFrameSchedulerSimple : public VideoChannelStateObserver, public WebrtcFrameScheduler { public: @@ -53,6 +54,9 @@ LeakyBucket pacing_bucket_; + // Set to true when a frame is being captured or encoded. + bool frame_pending_ = false; + // Set to true when encoding unchanged frames for top-off. bool top_off_is_active_ = false;
diff --git a/remoting/protocol/webrtc_frame_scheduler_unittest.cc b/remoting/protocol/webrtc_frame_scheduler_unittest.cc new file mode 100644 index 0000000..3a578e34 --- /dev/null +++ b/remoting/protocol/webrtc_frame_scheduler_unittest.cc
@@ -0,0 +1,68 @@ +// 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 "remoting/protocol/webrtc_frame_scheduler.h" + +#include "base/test/test_simple_task_runner.h" +#include "base/threading/thread_task_runner_handle.h" +#include "remoting/protocol/webrtc_dummy_video_encoder.h" +#include "remoting/protocol/webrtc_frame_scheduler_simple.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace remoting { +namespace protocol { + +class WebrtcFrameSchedulerTest : public ::testing::Test { + public: + WebrtcFrameSchedulerTest() + : task_runner_(new base::TestSimpleTaskRunner()), + task_runner_handle_(task_runner_.get()) { + video_encoder_factory_.reset(new WebrtcDummyVideoEncoderFactory()); + scheduler_.reset(new WebrtcFrameSchedulerSimple()); + scheduler_->Start(video_encoder_factory_.get(), + base::Bind(&WebrtcFrameSchedulerTest::CaptureCallback, + base::Unretained(this))); + } + ~WebrtcFrameSchedulerTest() override {} + + void CaptureCallback() { capture_callback_called_ = true; } + + void VerifyCaptureCallbackCalled() { + EXPECT_TRUE(capture_callback_called_); + capture_callback_called_ = false; + } + + protected: + scoped_refptr<base::TestSimpleTaskRunner> task_runner_; + base::ThreadTaskRunnerHandle task_runner_handle_; + + std::unique_ptr<WebrtcDummyVideoEncoderFactory> video_encoder_factory_; + std::unique_ptr<WebrtcFrameScheduler> scheduler_; + + bool capture_callback_called_ = false; +}; + +TEST_F(WebrtcFrameSchedulerTest, UpdateBitrateWhenPending) { + auto video_channel_observer = + video_encoder_factory_->get_video_channel_state_observer_for_tests(); + + video_channel_observer->OnKeyFrameRequested(); + video_channel_observer->OnTargetBitrateChanged(100); + + EXPECT_TRUE(task_runner_->HasPendingTask()); + task_runner_->RunPendingTasks(); + + VerifyCaptureCallbackCalled(); + + video_channel_observer->OnTargetBitrateChanged(1001); + + // |task_runner_| shouldn't have pending tasks as the scheduler should be + // waiting for the previous capture request to complete. + EXPECT_FALSE(task_runner_->HasPendingTask()); +} + +// TODO(sergeyu): Add more unittests. + +} // namespace protocol +} // namespace remoting
diff --git a/remoting/protocol/webrtc_video_stream.cc b/remoting/protocol/webrtc_video_stream.cc index 46a6f7e..dcc4e4c 100644 --- a/remoting/protocol/webrtc_video_stream.cc +++ b/remoting/protocol/webrtc_video_stream.cc
@@ -231,7 +231,7 @@ frame.timestamps->capture_started_time - frame.timestamps->input_event_timestamps.host_timestamp; stats.latest_event_timestamp = - frame.timestamps->input_event_timestamps.host_timestamp; + frame.timestamps->input_event_timestamps.client_timestamp; } stats.capture_delay = frame.timestamps->capture_delay;
diff --git a/services/service_manager/manifest.json b/services/service_manager/manifest.json index 0d34e24c..8e4ba92 100644 --- a/services/service_manager/manifest.json +++ b/services/service_manager/manifest.json
@@ -32,7 +32,9 @@ ] }, "required": { - "service:service_manager": [ "service_manager:all_users" ] + "*": [ "service_manager:service_factory" ], + "service:catalog": [ "service_manager:resolver" ], + "service:tracing": [ "app" ] } } }
diff --git a/services/service_manager/service_manager.cc b/services/service_manager/service_manager.cc index aa46c32..59ab9430 100644 --- a/services/service_manager/service_manager.cc +++ b/services/service_manager/service_manager.cc
@@ -506,6 +506,7 @@ "service_manager::mojom::ServiceManager"); spec.requires["*"].insert("service_manager:service_factory"); spec.requires["service:catalog"].insert("service_manager:resolver"); + spec.requires["service:tracing"].insert("app"); service_manager_instance_ = CreateInstance(Identity(), CreateServiceManagerIdentity(), spec);
diff --git a/services/service_manager/standalone/context.cc b/services/service_manager/standalone/context.cc index 55bc9f76..ed5c5f0 100644 --- a/services/service_manager/standalone/context.cc +++ b/services/service_manager/standalone/context.cc
@@ -36,6 +36,7 @@ #include "services/catalog/catalog.h" #include "services/catalog/store.h" #include "services/service_manager/connect_params.h" +#include "services/service_manager/connect_util.h" #include "services/service_manager/public/cpp/names.h" #include "services/service_manager/runner/common/switches.h" #include "services/service_manager/runner/host/in_process_native_runner.h" @@ -73,27 +74,6 @@ DISALLOW_COPY_AND_ASSIGN(Setup); }; -class TracingInterfaceProvider : public mojom::InterfaceProvider { - public: - explicit TracingInterfaceProvider(Tracer* tracer) : tracer_(tracer) {} - ~TracingInterfaceProvider() override {} - - // mojom::InterfaceProvider: - void GetInterface(const std::string& interface_name, - mojo::ScopedMessagePipeHandle client_handle) override { - if (tracer_ && interface_name == tracing::mojom::Provider::Name_) { - tracer_->ConnectToProvider( - mojo::MakeRequest<tracing::mojom::Provider>( - std::move(client_handle))); - } - } - - private: - Tracer* tracer_; - - DISALLOW_COPY_AND_ASSIGN(TracingInterfaceProvider); -}; - const size_t kMaxBlockingPoolThreads = 3; std::unique_ptr<base::Thread> CreateIOThread(const char* name) { @@ -202,23 +182,18 @@ } } - mojom::InterfaceProviderPtr tracing_remote_interfaces; - mojom::InterfaceProviderPtr tracing_local_interfaces; - mojo::MakeStrongBinding(base::MakeUnique<TracingInterfaceProvider>(&tracer_), - mojo::GetProxy(&tracing_local_interfaces)); - std::unique_ptr<ConnectParams> params(new ConnectParams); - params->set_source(CreateServiceManagerIdentity()); - params->set_target(Identity("service:tracing", mojom::kRootUserID)); - params->set_remote_interfaces(mojo::GetProxy(&tracing_remote_interfaces)); - service_manager_->Connect(std::move(params)); + Identity source_identity = CreateServiceManagerIdentity(); + Identity tracing_identity("service:tracing", mojom::kRootUserID); + tracing::mojom::FactoryPtr factory; + ConnectToInterface(service_manager(), source_identity, tracing_identity, + &factory); + provider_.InitializeWithFactory(&factory); if (command_line.HasSwitch(tracing::kTraceStartup)) { tracing::mojom::CollectorPtr coordinator; - auto coordinator_request = GetProxy(&coordinator); - tracing_remote_interfaces->GetInterface( - tracing::mojom::Collector::Name_, - coordinator_request.PassMessagePipe()); + ConnectToInterface(service_manager(), source_identity, tracing_identity, + &coordinator); tracer_.StartCollectingFromTracingService(std::move(coordinator)); } @@ -226,9 +201,8 @@ if (base::CommandLine::ForCurrentProcess()->HasSwitch( tracing::kEnableStatsCollectionBindings)) { tracing::mojom::StartupPerformanceDataCollectorPtr collector; - tracing_remote_interfaces->GetInterface( - tracing::mojom::StartupPerformanceDataCollector::Name_, - mojo::GetProxy(&collector).PassMessagePipe()); + ConnectToInterface(service_manager(), source_identity, tracing_identity, + &collector); #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX) // CurrentProcessInfo::CreationTime is only defined on some platforms. const base::Time creation_time = base::CurrentProcessInfo::CreationTime();
diff --git a/services/service_manager/standalone/context.h b/services/service_manager/standalone/context.h index 41b261fd..4522d39d 100644 --- a/services/service_manager/standalone/context.h +++ b/services/service_manager/standalone/context.h
@@ -15,6 +15,7 @@ #include "mojo/edk/embedder/process_delegate.h" #include "services/service_manager/service_manager.h" #include "services/service_manager/standalone/tracer.h" +#include "services/tracing/public/cpp/provider.h" namespace base { class SingleThreadTaskRunner; @@ -72,6 +73,7 @@ // Ensure this is destructed before task_runners_ since it owns a message pipe // that needs the IO thread to destruct cleanly. Tracer tracer_; + tracing::Provider provider_; std::unique_ptr<catalog::Catalog> catalog_; std::unique_ptr<ServiceManager> service_manager_; base::Time main_entry_time_;
diff --git a/services/service_manager/standalone/tracer.cc b/services/service_manager/standalone/tracer.cc index 0fa74a3..20fd888 100644 --- a/services/service_manager/standalone/tracer.cc +++ b/services/service_manager/standalone/tracer.cc
@@ -64,10 +64,6 @@ StopTracingAndFlushToDisk(); } -void Tracer::ConnectToProvider(tracing::mojom::ProviderRequest request) { - provider_.Bind(std::move(request)); -} - void Tracer::StopTracingAndFlushToDisk() { tracing_ = false; trace_file_ = fopen(trace_filename_.c_str(), "w+");
diff --git a/services/service_manager/standalone/tracer.h b/services/service_manager/standalone/tracer.h index 10bc60bf..3ee7986 100644 --- a/services/service_manager/standalone/tracer.h +++ b/services/service_manager/standalone/tracer.h
@@ -15,7 +15,6 @@ #include "base/macros.h" #include "base/memory/ref_counted_memory.h" #include "mojo/common/data_pipe_drainer.h" -#include "services/tracing/public/cpp/provider.h" #include "services/tracing/public/interfaces/tracing.mojom.h" namespace service_manager { @@ -78,7 +77,6 @@ tracing::mojom::CollectorPtr coordinator_; std::unique_ptr<mojo::common::DataPipeDrainer> drainer_; - tracing::Provider provider_; // Whether we're currently tracing. bool tracing_; // Categories to trace.
diff --git a/services/tracing/public/cpp/provider.cc b/services/tracing/public/cpp/provider.cc index 47e0ab0c..66e3a70 100644 --- a/services/tracing/public/cpp/provider.cc +++ b/services/tracing/public/cpp/provider.cc
@@ -42,24 +42,10 @@ StopTracing(); } -void Provider::Initialize(service_manager::Connector* connector, - const std::string& url) { - { - base::AutoLock lock(g_singleton_lock.Get()); - if (g_tracing_singleton_created) - return; - g_tracing_singleton_created = true; - } - - // This will only set the name for the first app in a loaded mojo file. It's - // up to something like CoreServices to name its own child threads. - base::PlatformThread::SetName(url); - - mojom::FactoryPtr factory; - connector->ConnectToInterface("service:tracing", &factory); +void Provider::InitializeWithFactoryInternal(mojom::FactoryPtr* factory) { mojom::ProviderPtr provider; Bind(GetProxy(&provider)); - factory->CreateRecorder(std::move(provider)); + (*factory)->CreateRecorder(std::move(provider)); #ifdef NDEBUG if (base::CommandLine::ForCurrentProcess()->HasSwitch( tracing::kEarlyTracing)) { @@ -70,6 +56,32 @@ #endif } +void Provider::InitializeWithFactory(mojom::FactoryPtr* factory) { + { + base::AutoLock lock(g_singleton_lock.Get()); + if (g_tracing_singleton_created) + return; + g_tracing_singleton_created = true; + } + InitializeWithFactoryInternal(factory); +} + +void Provider::Initialize(service_manager::Connector* connector, + const std::string& url) { + { + base::AutoLock lock(g_singleton_lock.Get()); + if (g_tracing_singleton_created) + return; + g_tracing_singleton_created = true; + } + mojom::FactoryPtr factory; + connector->ConnectToInterface("service:tracing", &factory); + InitializeWithFactoryInternal(&factory); + // This will only set the name for the first app in a loaded mojo file. It's + // up to something like CoreServices to name its own child threads. + base::PlatformThread::SetName(url); +} + void Provider::Bind(mojom::ProviderRequest request) { if (!binding_.is_bound()) { binding_.Bind(std::move(request));
diff --git a/services/tracing/public/cpp/provider.h b/services/tracing/public/cpp/provider.h index 4b305d4..761efec 100644 --- a/services/tracing/public/cpp/provider.h +++ b/services/tracing/public/cpp/provider.h
@@ -24,12 +24,15 @@ Provider(); ~Provider() override; + void InitializeWithFactory(mojom::FactoryPtr* factory); void Initialize(service_manager::Connector* connector, const std::string& url); void Bind(mojom::ProviderRequest request); private: + void InitializeWithFactoryInternal(mojom::FactoryPtr* factory); + // mojom::Provider implementation: void StartTracing(const std::string& categories, mojom::RecorderPtr recorder) override;
diff --git a/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter b/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter index 7418bb64..ec4b767 100644 --- a/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter +++ b/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter
@@ -1,9 +1,6 @@ -ActivityLogApiTest.TriggerEvent -BrowserActionApiTest.BrowserActionPopupWithIframe -ChromeServiceWorker* --ContinueWhereILeftOffTest.PostWithPassword --ContinueWhereILeftOffTest.PostWithPasswordBrowserClose --ContinueWhereILeftOffTest.PostWithPasswordCloseAllBrowsers -CrazyExtensionTest.Crazy -DevToolsSanityTest.TestNetworkRawHeadersText -ErrorPageTest.IFrameDNSError_JavaScript @@ -16,10 +13,9 @@ -ExtensionBindingsApiTest.ModuleSystem -ExtensionBrowserTest.WindowOpenExtension -ExtensionResourceRequestPolicyTest.Iframe --ExtensionWebRequestApiTest.WebRequestComplex +# ExtensionWebRequestApiTest.WebRequestDeclarative2 crashes on bots but passes locally -ExtensionWebRequestApiTest.WebRequestDeclarative2 -ExtensionWebRequestApiTest.WebRequestUnloadImmediately --ExtensionWebRequestApiTest.WebRequestWithWithheldPermissions -ExternallyConnectableMessagingTest.FromPopup -FindInPageControllerTest.SearchWithinSpecialURL -IsolatedAppTest.CookieIsolation @@ -36,7 +32,6 @@ -PrerenderBrowserTest.PrerenderCrossProcessServerRedirect -PrerenderBrowserTest.PrerenderCrossProcessServerRedirectNoHang -RedirectTest.ClientServerServer --RestartTest.PostWithPassword -SafeBrowsing* -SubresourceFilterBrowserTest.MainFrameActivation -SubresourceFilterBrowserTest.MainFrameActivationOnStartup @@ -58,25 +53,16 @@ -WebNavigationApiTest.RequestOpenTab -WebNavigationApiTest.SrcDoc -WebNavigationApiTest.UserAction --WebViewTests/WebViewTest.Shim_TestDeclarativeWebRequestAPI/* --WebViewTests/WebViewTest.Shim_TestDeclarativeWebRequestAPISendMessage/* -WebViewTests/WebViewTest.Shim_TestInlineScriptFromAccessibleResources/* -WebViewTests/WebViewTest.Shim_TestMailtoLink/* -WebViewTests/WebViewTest.Shim_TestNavigationToExternalProtocol/* -WebViewTests/WebViewTest.Shim_TestNestedSubframes/* --WebViewTests/WebViewTest.Shim_TestWebRequestAPIErrorOccurred/* --WebViewTests/WebViewTest.Shim_TestWebRequestAPIGoogleProperty/* --WebViewTests/WebViewTest.Shim_TestWebRequestAPIWithHeaders/* --WebViewTests/WebViewTest.Shim_TestWebRequestListenerSurvivesReparenting/* -WebViewTests/WebViewTest.Shim_TestWebViewInsideFrame/* # Errors related to forms submission. -PhishingDOMFeatureExtractorTest.SubFrames -PhishingDOMFeatureExtractorTest.SubframeRemoval -# https://crbug.com/647712: Nested URL blocking needs to work with PlzNavigate --ProcessManagerBrowserTest.NestedURLNavigationsToExtensionBlocked - # Fail in Debug. -BookmarkBubbleSignInDelegateTest.* -GetAuthTokenFunctionTest.ComponentWithNormalClientId
diff --git a/testing/libfuzzer/efficient_fuzzer.md b/testing/libfuzzer/efficient_fuzzer.md index fa1bc88a..fa7efa9 100644 --- a/testing/libfuzzer/efficient_fuzzer.md +++ b/testing/libfuzzer/efficient_fuzzer.md
@@ -186,18 +186,28 @@ ## Coverage -You can easily generate source-level coverage report for a given corpus: +[ClusterFuzz status] page provides fuzzer source-level coverage report +from the recent run. Looking at the report might provide an insight +to improve fuzzer coverage. -``` -ASAN_OPTIONS=html_cov_report=1:sancov_path=./third_party/llvm-build/Release+Asserts/bin/sancov \ - ./out/libfuzzer/my_fuzzer -runs=0 ~/tmp/my_fuzzer_corpus -``` +You can also access source-level coverage report locally: -This will produce an .html file with colored source-code. It can be used to -determine where your fuzzer is "stuck". Replace `ASAN_OPTIONS` by corresponding -option variable if your are using another sanitizer (e.g. `MSAN_OPTIONS`). -`sancov_path` can be omitted by adding llvm bin directory to `PATH` environment -variable. +```bash +# produces binary .sancov file +ASAN_OPTIONS=coverage=1 ./out/libfuzzer/my_fuzzer -runs=0 ~/tmp/my_fuzzer_corpus +# Convert binary .sancov to symbolized .symcov file. +./third_party/llvm-build/Release+Asserts/bin/sancov \ + -symbolize my_fuzzer my_fuzzer.123.sancov > my_fuzzer.symcov +# Launch coverage report server +curl http://llvm.org/svn/llvm-project/llvm/trunk/tools/sancov/coverage-report-server.py | python3 \ + --symcov my_fuzzer.symcov --srcpath path_to_chromium_sources +# Navigate to http://localhost:8001/ to view coverage report +``` +Replace `ASAN_OPTIONS` by corresponding option variable if your are using +another sanitizer (e.g. `MSAN_OPTIONS`). + +*NOTE: This is an experimental feature and an active area of work. We are +working on improving this process.* ## Corpus Size
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process index fe732fa2..83ea7fb 100644 --- a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process +++ b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
@@ -95,6 +95,9 @@ # layout_test_runtime_flags_.have_top_loading_frame() crbug.com/645641 imported/wpt/html/syntax/parsing/html5lib_tests19.html [ Crash Failure ] +# https://crbug.com/657081 - Allow-CSP-From header test (experimental feature) +crbug.com/657081 http/tests/security/contentSecurityPolicy/embeddedEnforcement/allow_csp_from-header.html [ Failure ] + # TODO(lukasza, alexmos): Triage these failures. Bug(none) fast/frames/cached-frame-counter.html [ Timeout ] Bug(none) fast/frames/frame-limit.html [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/fast/performance/longtasktiming.html b/third_party/WebKit/LayoutTests/fast/performance/longtasktiming.html new file mode 100644 index 0000000..baa96a0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/performance/longtasktiming.html
@@ -0,0 +1,31 @@ +<!DOCTYPE html> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script> + +async_test(function (t) { + var observer = new PerformanceObserver( + t.step_func(function (entryList) { + var entries = entryList.getEntries(); + /* TODO(panicker): Update after fixing http://crbug.com/651461 */ + for (var i = 0; i < entries.length; i++) { + assert_equals(entries[i].entryType, "longtask", + "entryType expected to be: longtask"); + assert_equals(entries[i].name, "same-origin", + "name expected to be: same-origin"); + assert_greater_than(entries[i].duration, 50000, + "duration expected to be greater than 50ms threshold"); + } + observer.disconnect(); + t.done(); + }) + ); + observer.observe({entryTypes: ["longtask"]}); + + /* Generate a slow task */ + var begin = window.performance.now(); + while (window.performance.now() < begin + 51); + +}, "Performance longtask entries are observable"); + +</script>
diff --git a/third_party/WebKit/LayoutTests/fragmentation/break-in-first-table-row-only-expected.txt b/third_party/WebKit/LayoutTests/fragmentation/break-in-first-table-row-only-expected.txt new file mode 100644 index 0000000..a28420d --- /dev/null +++ b/third_party/WebKit/LayoutTests/fragmentation/break-in-first-table-row-only-expected.txt
@@ -0,0 +1,12 @@ +There should be a hotpink square below. + + + + + + + + + + +PASS
diff --git a/third_party/WebKit/LayoutTests/fragmentation/break-in-first-table-row-only.html b/third_party/WebKit/LayoutTests/fragmentation/break-in-first-table-row-only.html new file mode 100644 index 0000000..72c164f --- /dev/null +++ b/third_party/WebKit/LayoutTests/fragmentation/break-in-first-table-row-only.html
@@ -0,0 +1,33 @@ +<!DOCTYPE html> +<style> + body { overflow: scroll; } +</style> +<p>There should be a hotpink square below.</p> +<div id="multicol" style="position:relative; columns:2; column-fill:auto; column-gap:0; width:100px; height:90px; line-height:20px; orphans:1; widows:1;"> + <br> + <br> + <br> + <table data-expected-height="110" cellspacing="0" cellpadding="0"> + <col style="width:10px;"> + <col style="width:10px;"> + <tr data-expected-height="50"> + <td data-expected-height="50"> + <br><br> + </td> + </tr> + <tr data-expected-height="60"> + <td data-expected-height="60"> + <br> + <div data-offset-y="40" style="position:relative; background:hotpink;"><br></div> + <br> + </td> + <td> + <div data-offset-y="40" style="position:relative; height:20px; background:hotpink;"><br></div> + </td> + </tr> + </table> +</div> +<script src="../resources/check-layout.js"></script> +<script> + checkLayout("#multicol"); +</script>
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-cyclic-ref-expected.txt b/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-cyclic-reference-expected.txt similarity index 80% rename from third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-cyclic-ref-expected.txt rename to third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-cyclic-reference-expected.txt index 7354e1ac..1980c439 100644 --- a/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-cyclic-ref-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-cyclic-reference-expected.txt
@@ -1,8 +1,6 @@ Tests that debugging a page where Object prototype has a cyclic reference won't crash the browser.Bug 43558 -Debugger was enabled. Set timer for test function. Script execution paused. Script execution resumed. -Debugger was disabled.
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-cyclic-ref.html b/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-cyclic-reference.html similarity index 100% rename from third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-cyclic-ref.html rename to third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-cyclic-reference.html
diff --git a/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-clear-key-invalid-license.html b/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-clear-key-invalid-license.html index 55635f40..d28e520 100644 --- a/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-clear-key-invalid-license.html +++ b/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-clear-key-invalid-license.html
@@ -20,7 +20,7 @@ assert_unreached('Error: update() succeeded unexpectedly.'); test.done(); }).catch(function(error) { - assert_equals(error.name, 'InvalidAccessError'); + assert_equals(error.name, 'TypeError'); test.done(); }); }
diff --git a/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-clearkey-update-non-ascii-input.html b/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-clearkey-update-non-ascii-input.html index da04aed5..c7a39e4 100644 --- a/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-clearkey-update-non-ascii-input.html +++ b/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-clearkey-update-non-ascii-input.html
@@ -28,7 +28,7 @@ mediaKeySession.update(stringToUint8Array(jwkSet)).then(function() { forceTestFailureFromPromise(test, 'Error: update() succeeded'); }, function(error) { - assert_equals(error.name, 'InvalidAccessError'); + assert_equals(error.name, 'TypeError'); test.done(); }); }
diff --git a/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-generate-request-disallowed-input.html b/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-generate-request-disallowed-input.html index 9c1dac6..fc37312 100644 --- a/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-generate-request-disallowed-input.html +++ b/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-generate-request-disallowed-input.html
@@ -10,7 +10,7 @@ <script> // Create a session and call generateRequest() with |initDataType| // and |initData|. generateRequest() should fail with an - // InvalidAccessError. Returns a promise that resolves successfully + // TypeError. Returns a promise that resolves successfully // if the error happened, rejects otherwise. function test_session(initDataType, initData) { @@ -27,7 +27,7 @@ }).then(function() { assert_unreached('generateRequest() succeeded'); }, function(error) { - assert_equals(error.name, 'InvalidAccessError'); + assert_equals(error.name, 'TypeError'); return Promise.resolve('success'); }); })
diff --git a/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-not-callable-after-close.html b/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-not-callable-after-close.html new file mode 100644 index 0000000..9413cf75 --- /dev/null +++ b/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-not-callable-after-close.html
@@ -0,0 +1,112 @@ +<!DOCTYPE html> +<html> + <head> + <title>Test MediaKeySession not callable after close().</title> + <script src="encrypted-media-utils.js"></script> + <script src="../../resources/testharness.js"></script> + <script src="../../resources/testharnessreport.js"></script> + </head> + <body> + <script> + // After a MediaKeySession object is closed, other operations + // (except calling close() again) on the session are not allowed + // and should return an InvalidStateError. + + // Create, initialize, and close a session. Returns a promise that + // resolves with the created and closed mediaKeySession and the + // initDataType used. + function createClosedSession() { + var initDataType; + var initData; + var mediaKeySession; + + return navigator.requestMediaKeySystemAccess('org.w3.clearkey', getSimpleConfiguration()).then(function(access) { + initDataType = access.getConfiguration().initDataTypes[0]; + initData = getInitData(initDataType); + return access.createMediaKeys(); + }).then(function(mediaKeys) { + mediaKeySession = mediaKeys.createSession(); + return mediaKeySession.generateRequest(initDataType, initData); + }).then(function() { + return mediaKeySession.close(); + }).then(function(result) { + return Promise.resolve({ session: mediaKeySession, initDataType: initDataType} ); + }); + } + + promise_test(function() + { + return createClosedSession().then(function(result) { + var mediaKeySession = result.session; + var initDataType = result.initDataType; + var initData = getInitData(initDataType); + + // Now try the operation that should fail. + return mediaKeySession.generateRequest(initDataType, initData); + }).then(function() { + assert_unreached('Unexpected generateRequest() success.'); + }, function(error) { + assert_equals(error.name, 'InvalidStateError'); + return Promise.resolve(null); + }); + }, 'generateRequest() after close().'); + + promise_test(function() + { + return createClosedSession().then(function(result) { + var mediaKeySession = result.session; + + // Now try the operation that should fail. + return mediaKeySession.load('1234'); + }).then(function() { + assert_unreached('Unexpected load() success.'); + }, function(error) { + assert_equals(error.name, 'InvalidStateError'); + return Promise.resolve(null); + }); + }, 'load() after close().'); + + promise_test(function() + { + return createClosedSession().then(function(result) { + var mediaKeySession = result.session; + + // Now try the operation that should fail. + var validLicense = stringToUint8Array(createJWKSet(createJWK(stringToUint8Array('123'), stringToUint8Array('1234567890abcdef')))); + return mediaKeySession.update(validLicense); + }).then(function() { + assert_unreached('Unexpected update() success.'); + }, function(error) { + assert_equals(error.name, 'InvalidStateError'); + return Promise.resolve(null); + }); + }, 'update() after close().'); + + promise_test(function() + { + return createClosedSession().then(function(result) { + var mediaKeySession = result.session; + + // Now try the operation that should fail. + return mediaKeySession.remove(); + }).then(function() { + assert_unreached('Unexpected remove() success.'); + }, function(error) { + assert_equals(error.name, 'InvalidStateError'); + return Promise.resolve(null); + }); + }, 'remove() after close().'); + + promise_test(function() + { + return createClosedSession().then(function(result) { + var mediaKeySession = result.session; + + // Now try calling close() again, which should succeed. + return mediaKeySession.close(); + }); + }, 'close() after close().'); + </script> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-requestmediakeysystemaccess.html b/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-requestmediakeysystemaccess.html index a9b85da..49cd47e 100644 --- a/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-requestmediakeysystemaccess.html +++ b/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-requestmediakeysystemaccess.html
@@ -38,7 +38,7 @@ } // Tests for keySystem. - expect_error('', [{}], 'InvalidAccessError', 'Empty keySystem'); + expect_error('', [{}], 'TypeError', 'Empty keySystem'); expect_error('com.example.unsupported', [{}], 'NotSupportedError', 'Unsupported keySystem'); expect_error('org.w3.clearkey.', [{}], 'NotSupportedError', 'keySystem ends with "."'); expect_error('org.w3.ClearKey', [{}], 'NotSupportedError', 'Capitalized keySystem'); @@ -77,7 +77,7 @@ expect_error('Org.w3.clearkey', [{}], 'NotSupportedError', 'Key system name is case sensitive'); // Tests for trivial configurations. - expect_error('org.w3.clearkey', [], 'InvalidAccessError', 'Empty supportedConfigurations'); + expect_error('org.w3.clearkey', [], 'TypeError', 'Empty supportedConfigurations'); expect_config('org.w3.clearkey', [{}], {}, 'Empty configuration'); // Various combinations of supportedConfigurations.
diff --git a/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-syntax.html b/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-syntax.html index 201ee32..bae01004 100644 --- a/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-syntax.html +++ b/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-syntax.html
@@ -72,11 +72,11 @@ func: function() { return navigator.requestMediaKeySystemAccess(1, [{}]); } }, { - exception: 'InvalidAccessError', + exception: 'TypeError', func: function() { return navigator.requestMediaKeySystemAccess(new Uint8Array(0), [{}]); } }, { - exception: 'InvalidAccessError', + exception: 'TypeError', func: function() { return navigator.requestMediaKeySystemAccess('', [{}]); } }, { @@ -90,7 +90,7 @@ }, // Empty sequence of MediaKeySystemConfiguration. { - exception: 'InvalidAccessError', + exception: 'TypeError', func: function() { return navigator.requestMediaKeySystemAccess('org.w3.clearkey', []); } }, // Invalid sequences of MediaKeySystemConfigurations. @@ -283,7 +283,7 @@ }, // Invalid parameters. { - exception: 'InvalidAccessError', + exception: 'TypeError', func: function(mk8, _, initData) { return mk8.createSession().generateRequest('', initData); } }, // Not supported initDataTypes. @@ -300,7 +300,7 @@ func: function(mk11, _, initData) { return mk11.createSession().generateRequest(1, initData); } }, { - exception: 'InvalidAccessError', + exception: 'TypeError', func: function(mk12, _, initData) { return mk12.createSession().generateRequest(new Uint8Array(0), initData); } }, { @@ -356,7 +356,7 @@ func: function(mk5, type) { return mk5.createSession().generateRequest(type, 1); } }, { - exception: 'InvalidAccessError', + exception: 'TypeError', func: function(mk6, type) { return mk6.createSession().generateRequest(type, new Uint8Array(0)); } } ]; @@ -413,21 +413,21 @@ func: function(mk1) { return mk1.createSession('temporary').load(); } }, // 'temporary' sessions are never allowed, so always return - // 'InvalidAccessError'. + // 'TypeError'. { - exception: 'InvalidAccessError', + exception: 'TypeError', func: function(mk3) { return mk3.createSession('temporary').load(''); } }, { - exception: 'InvalidAccessError', + exception: 'TypeError', func: function(mk4) { return mk4.createSession('temporary').load(1); } }, { - exception: 'InvalidAccessError', + exception: 'TypeError', func: function(mk5) { return mk5.createSession('temporary').load('!@#$%^&*()'); } }, { - exception: 'InvalidAccessError', + exception: 'TypeError', func: function(mk6) { return mk6.createSession('temporary').load('1234'); } } ]; @@ -616,7 +616,7 @@ func: function(s) { return s.update(1); } }, { - exception: 'InvalidAccessError', + exception: 'TypeError', func: function(s) { return s.update(new Uint8Array(0)); } } ]; @@ -822,7 +822,7 @@ }).then(function(result) { assert_unreached('remove() should not succeed for temporary sessions'); }, function(error) { - assert_equals(error.name, 'InvalidAccessError'); + assert_equals(error.name, 'TypeError'); }); } @@ -933,7 +933,7 @@ }, // Empty array. { - exception: 'InvalidAccessError', + exception: 'TypeError', func: function(mk) { return mk.setServerCertificate(new Uint8Array(0)); } } ];
diff --git a/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-update-disallowed-input.html b/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-update-disallowed-input.html index e605bf66..2467aea 100644 --- a/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-update-disallowed-input.html +++ b/third_party/WebKit/LayoutTests/media/encrypted-media/encrypted-media-update-disallowed-input.html
@@ -48,7 +48,7 @@ mediaKeySession.update(jwkSetArray).then(function() { forceTestFailureFromPromise(test, 'Error: update() succeeded'); }, function(error) { - assert_equals(error.name, 'InvalidAccessError'); + assert_equals(error.name, 'TypeError'); test.done(); }); }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/list-marker-move-expected.html b/third_party/WebKit/LayoutTests/paint/invalidation/list-marker-move-expected.html new file mode 100644 index 0000000..6368453d4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/paint/invalidation/list-marker-move-expected.html
@@ -0,0 +1,5 @@ +<!DOCTYPE html> +Tests paint invalidation of list markers when the list is moved. +<ul id="ulist" style="position: absolute; top: 300px; width: 300px"> + <li>List item</li> +</ul>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/list-marker-move.html b/third_party/WebKit/LayoutTests/paint/invalidation/list-marker-move.html new file mode 100644 index 0000000..964a712 --- /dev/null +++ b/third_party/WebKit/LayoutTests/paint/invalidation/list-marker-move.html
@@ -0,0 +1,11 @@ +<!DOCTYPE html> +Tests paint invalidation of list markers when the list is moved. +<ul id="ulist" style="position: absolute; top: 100px; width: 300px"> + <li>List item</li> +</ul> +<script src="../../resources/run-after-layout-and-paint.js"></script> +<script> +runAfterLayoutAndPaint(function() { + ulist.style.top = '300px'; +}, true); +</script>
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/offset-change-wrong-invalidation-with-float-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/offset-change-wrong-invalidation-with-float-expected.txt index d37c032..d9674b2 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/offset-change-wrong-invalidation-with-float-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/offset-change-wrong-invalidation-with-float-expected.txt
@@ -15,6 +15,16 @@ "object": "LayoutBlockFlow (positioned) UL id='submenu'", "rect": [48, -156, 40, 20], "reason": "bounds change" + }, + { + "object": "LayoutListMarker (anonymous)", + "rect": [70, 94, 7, 19], + "reason": "bounds change" + }, + { + "object": "LayoutListMarker (anonymous)", + "rect": [70, -156, 7, 19], + "reason": "bounds change" } ] }, @@ -64,7 +74,7 @@ }, { "object": "LayoutListMarker (anonymous)", - "reason": "location change" + "reason": "bounds change" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/offset-change-wrong-invalidation-with-float-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/offset-change-wrong-invalidation-with-float-expected.txt index a044310a..1cbc2db7 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/offset-change-wrong-invalidation-with-float-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/offset-change-wrong-invalidation-with-float-expected.txt
@@ -15,6 +15,16 @@ "object": "LayoutBlockFlow (positioned) UL id='submenu'", "rect": [48, -158, 40, 18], "reason": "bounds change" + }, + { + "object": "LayoutListMarker (anonymous)", + "rect": [71, 92, 7, 18], + "reason": "bounds change" + }, + { + "object": "LayoutListMarker (anonymous)", + "rect": [71, -158, 7, 18], + "reason": "bounds change" } ] }, @@ -64,7 +74,7 @@ }, { "object": "LayoutListMarker (anonymous)", - "reason": "location change" + "reason": "bounds change" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/offset-change-wrong-invalidation-with-float-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/offset-change-wrong-invalidation-with-float-expected.txt index e7344e8..f87ab4c 100644 --- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/offset-change-wrong-invalidation-with-float-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/offset-change-wrong-invalidation-with-float-expected.txt
@@ -15,6 +15,16 @@ "object": "LayoutBlockFlow (positioned) UL id='submenu'", "rect": [48, -158, 40, 18], "reason": "bounds change" + }, + { + "object": "LayoutListMarker (anonymous)", + "rect": [71, 92, 7, 17], + "reason": "bounds change" + }, + { + "object": "LayoutListMarker (anonymous)", + "rect": [71, -158, 7, 17], + "reason": "bounds change" } ] }, @@ -64,7 +74,7 @@ }, { "object": "LayoutListMarker (anonymous)", - "reason": "location change" + "reason": "bounds change" } ] }
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitor.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitor.cpp index 1ed50fa..b72b7ff 100644 --- a/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitor.cpp +++ b/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitor.cpp
@@ -27,6 +27,9 @@ void ScriptWrappableVisitor::TracePrologue( v8::EmbedderReachableReferenceReporter* reporter) { + // This CHECK ensures that wrapper tracing is not started from scopes + // that forbid GC execution, e.g., constructors. + CHECK(!ThreadState::current()->isGCForbidden()); performCleanup(); DCHECK(!m_tracingInProgress);
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 8144745..e9cd65b 100644 --- a/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValueFuzzer.cpp +++ b/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValueFuzzer.cpp
@@ -10,6 +10,7 @@ #include "core/testing/DummyPageHolder.h" #include "platform/RuntimeEnabledFeatures.h" #include "platform/testing/BlinkFuzzerTestSupport.h" +#include "platform/testing/UnitTestHelpers.h" #include "public/platform/WebBlobInfo.h" #include "public/platform/WebMessagePortChannel.h" #include "wtf/StringHasher.h" @@ -93,9 +94,16 @@ RefPtr<SerializedScriptValue> serializedScriptValue = SerializedScriptValue::create(reinterpret_cast<const char*>(data), size); serializedScriptValue->deserialize(isolate, messagePorts, blobs); - - // Clean up. CHECK(!tryCatch.HasCaught()) << "deserialize() should return null rather than throwing an exception."; + + // Clean up. We have to periodically run pending tasks so that scheduled + // Oilpan GC occurs. + static int iterations = 0; + if (iterations++ == 2048) { + testing::runPendingTasks(); + iterations = 0; + } + return 0; }
diff --git a/third_party/WebKit/Source/core/dom/ContainerNode.h b/third_party/WebKit/Source/core/dom/ContainerNode.h index 9f5e2c5b..ea945df 100644 --- a/third_party/WebKit/Source/core/dom/ContainerNode.h +++ b/third_party/WebKit/Source/core/dom/ContainerNode.h
@@ -26,6 +26,7 @@ #define ContainerNode_h #include "bindings/core/v8/ExceptionStatePlaceholder.h" +#include "bindings/core/v8/ScriptWrappableVisitor.h" #include "core/CoreExport.h" #include "core/dom/Node.h" #include "core/html/CollectionType.h" @@ -311,8 +312,14 @@ const QualifiedName* attrName = nullptr, Element* attributeOwnerElement = nullptr); - void setFirstChild(Node* child) { m_firstChild = child; } - void setLastChild(Node* child) { m_lastChild = child; } + void setFirstChild(Node* child) { + m_firstChild = child; + ScriptWrappableVisitor::writeBarrier(this, m_firstChild); + } + void setLastChild(Node* child) { + m_lastChild = child; + ScriptWrappableVisitor::writeBarrier(this, m_lastChild); + } // Utility functions for NodeListsNodeData API. template <typename Collection>
diff --git a/third_party/WebKit/Source/core/dom/DOMMatrix.cpp b/third_party/WebKit/Source/core/dom/DOMMatrix.cpp index eed0a17..0e77259c 100644 --- a/third_party/WebKit/Source/core/dom/DOMMatrix.cpp +++ b/third_party/WebKit/Source/core/dom/DOMMatrix.cpp
@@ -87,6 +87,25 @@ m_is2D = value; } +void DOMMatrix::setNAN() { + m_matrix->setM11(NAN); + m_matrix->setM12(NAN); + m_matrix->setM13(NAN); + m_matrix->setM14(NAN); + m_matrix->setM21(NAN); + m_matrix->setM22(NAN); + m_matrix->setM23(NAN); + m_matrix->setM24(NAN); + m_matrix->setM31(NAN); + m_matrix->setM32(NAN); + m_matrix->setM33(NAN); + m_matrix->setM34(NAN); + m_matrix->setM41(NAN); + m_matrix->setM42(NAN); + m_matrix->setM43(NAN); + m_matrix->setM44(NAN); +} + DOMMatrix* DOMMatrix::multiplySelf(DOMMatrixInit& other, ExceptionState& exceptionState) { DOMMatrix* otherMatrix = DOMMatrix::fromMatrix(other, exceptionState); @@ -190,22 +209,7 @@ if (m_matrix->isInvertible()) { m_matrix = TransformationMatrix::create(m_matrix->inverse()); } else { - setM11(NAN); - setM12(NAN); - setM13(NAN); - setM14(NAN); - setM21(NAN); - setM22(NAN); - setM23(NAN); - setM24(NAN); - setM31(NAN); - setM32(NAN); - setM33(NAN); - setM34(NAN); - setM41(NAN); - setM42(NAN); - setM43(NAN); - setM44(NAN); + setNAN(); setIs2D(false); } return this;
diff --git a/third_party/WebKit/Source/core/dom/DOMMatrix.h b/third_party/WebKit/Source/core/dom/DOMMatrix.h index 782d3ae..0508392 100644 --- a/third_party/WebKit/Source/core/dom/DOMMatrix.h +++ b/third_party/WebKit/Source/core/dom/DOMMatrix.h
@@ -106,6 +106,7 @@ DOMMatrix(T sequence, int size); void setIs2D(bool value); + void setNAN(); }; } // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/LiveNodeList.h b/third_party/WebKit/Source/core/dom/LiveNodeList.h index fd3110d..f7501dae 100644 --- a/third_party/WebKit/Source/core/dom/LiveNodeList.h +++ b/third_party/WebKit/Source/core/dom/LiveNodeList.h
@@ -46,7 +46,12 @@ : LiveNodeListBase(ownerNode, rootType, invalidationType, - collectionType) {} + collectionType) { + // Keep this in the child class because |registerNodeList| requires wrapper + // tracing and potentially calls virtual methods which is not allowed in a + // base class constructor. + document().registerNodeList(this); + } unsigned length() const final; Element* item(unsigned offset) const final;
diff --git a/third_party/WebKit/Source/core/dom/LiveNodeListBase.h b/third_party/WebKit/Source/core/dom/LiveNodeListBase.h index cdc229e..e121e68 100644 --- a/third_party/WebKit/Source/core/dom/LiveNodeListBase.h +++ b/third_party/WebKit/Source/core/dom/LiveNodeListBase.h
@@ -52,8 +52,6 @@ DCHECK_EQ(m_rootType, static_cast<unsigned>(rootType)); DCHECK_EQ(m_invalidationType, static_cast<unsigned>(invalidationType)); DCHECK_EQ(m_collectionType, static_cast<unsigned>(collectionType)); - - document().registerNodeList(this); } virtual ~LiveNodeListBase() {}
diff --git a/third_party/WebKit/Source/core/frame/Settings.in b/third_party/WebKit/Source/core/frame/Settings.in index 198c3a5..6892fa4 100644 --- a/third_party/WebKit/Source/core/frame/Settings.in +++ b/third_party/WebKit/Source/core/frame/Settings.in
@@ -217,13 +217,10 @@ # crbug.com/304873 tracks removal once it's been enabled on all platforms. touchEditingEnabled initial=false -# Settings for experimental desktop pinch-zoom support (with semantics -# optimized for large screens). Pinch-zoom generally is implemented mainly -# outside of blink (in the compositor) and doesn't require any settings. -# These settings are for an experimental modification to how pinch-zoom -# behaves. crbug.com/304869 tracks removal. +# If true, scrollers will use overlay scrollbars. These do not take up any +# layout width, are drawn using solid color quads by the compositor, and fade away +# after a timeout. useSolidColorScrollbars initial=false -pinchOverlayScrollbarThickness type=int, initial=0 # Experiment to have all APIs reflect the layout viewport. # crbug.com/489206 tracks the experiment. @@ -432,4 +429,4 @@ # Spellchecking is enabled by default for elements that do not specify it explicitly # using the "spellcheck" attribute. -spellCheckEnabledByDefault initial=true \ No newline at end of file +spellCheckEnabledByDefault initial=true
diff --git a/third_party/WebKit/Source/core/frame/csp/CSPSourceList.cpp b/third_party/WebKit/Source/core/frame/csp/CSPSourceList.cpp index dd061cd1..e429ce978f 100644 --- a/third_party/WebKit/Source/core/frame/csp/CSPSourceList.cpp +++ b/third_party/WebKit/Source/core/frame/csp/CSPSourceList.cpp
@@ -46,14 +46,15 @@ bool CSPSourceList::matches( const KURL& url, ResourceRequest::RedirectStatus redirectStatus) const { - // Wildcards match network schemes ('http', 'https', 'ws', 'wss'), and the - // scheme of the protected resource: - // https://w3c.github.io/webappsec-csp/#match-url-to-source-expression. Other + // Wildcards match network schemes ('http', 'https', 'ftp', 'ws', 'wss'), and + // the scheme of the protected resource: + // https://w3c.github.io/webappsec-csp/#match-url-to-source-expression. Other // schemes, including custom schemes, must be explicitly listed in a source // list. if (m_allowStar) { - if (url.protocolIsInHTTPFamily() || url.protocolIs("ws") || - url.protocolIs("wss") || m_policy->protocolMatchesSelf(url)) + if (url.protocolIsInHTTPFamily() || url.protocolIs("ftp") || + url.protocolIs("ws") || url.protocolIs("wss") || + m_policy->protocolMatchesSelf(url)) return true; return hasSourceMatchInList(url, redirectStatus);
diff --git a/third_party/WebKit/Source/core/frame/csp/CSPSourceListTest.cpp b/third_party/WebKit/Source/core/frame/csp/CSPSourceListTest.cpp index 0c611d8c..f75e95a 100644 --- a/third_party/WebKit/Source/core/frame/csp/CSPSourceListTest.cpp +++ b/third_party/WebKit/Source/core/frame/csp/CSPSourceListTest.cpp
@@ -75,6 +75,7 @@ EXPECT_TRUE(sourceList.matches(KURL(base, "http://example.com/bar"))); EXPECT_TRUE(sourceList.matches(KURL(base, "http://foo.example.com/"))); EXPECT_TRUE(sourceList.matches(KURL(base, "http://foo.example.com/bar"))); + EXPECT_TRUE(sourceList.matches(KURL(base, "ftp://example.com/"))); EXPECT_FALSE(sourceList.matches(KURL(base, "data:https://example.test/"))); EXPECT_FALSE(sourceList.matches(KURL(base, "blob:https://example.test/")));
diff --git a/third_party/WebKit/Source/core/html/HTMLCollection.cpp b/third_party/WebKit/Source/core/html/HTMLCollection.cpp index 53eab77..10a1735 100644 --- a/third_party/WebKit/Source/core/html/HTMLCollection.cpp +++ b/third_party/WebKit/Source/core/html/HTMLCollection.cpp
@@ -169,7 +169,12 @@ type), m_overridesItemAfter(itemAfterOverrideType == OverridesItemAfter), m_shouldOnlyIncludeDirectChildren( - shouldTypeOnlyIncludeDirectChildren(type)) {} + shouldTypeOnlyIncludeDirectChildren(type)) { + // Keep this in the child class because |registerNodeList| requires wrapper + // tracing and potentially calls virtual methods which is not allowed in a + // base class constructor. + document().registerNodeList(this); +} HTMLCollection* HTMLCollection::create(ContainerNode& base, CollectionType type) {
diff --git a/third_party/WebKit/Source/core/html/HTMLInputElement.cpp b/third_party/WebKit/Source/core/html/HTMLInputElement.cpp index 7ee21b5..232fda4 100644 --- a/third_party/WebKit/Source/core/html/HTMLInputElement.cpp +++ b/third_party/WebKit/Source/core/html/HTMLInputElement.cpp
@@ -78,6 +78,7 @@ namespace blink { +using ValueMode = InputType::ValueMode; using namespace HTMLNames; class ListAttributeTargetObserver : public IdTargetObserver { @@ -409,7 +410,8 @@ InputType* newType = InputType::create(*this, newTypeName); removeFromRadioButtonGroup(); - bool didStoreValue = m_inputType->storesValueSeparateFromAttribute(); + bool didStoreValue = m_inputType->valueMode() == ValueMode::kValue || + m_inputType->valueMode() == ValueMode::kFilename; bool didRespectHeightAndWidth = m_inputType->shouldRespectHeightAndWidthAttributes(); bool couldBeSuccessfulSubmitButton = canBeSuccessfulSubmitButton(); @@ -423,7 +425,8 @@ setNeedsWillValidateCheck(); - bool willStoreValue = m_inputType->storesValueSeparateFromAttribute(); + bool willStoreValue = m_inputType->valueMode() == ValueMode::kValue || + m_inputType->valueMode() == ValueMode::kFilename; // https://html.spec.whatwg.org/multipage/forms.html#input-type-change // @@ -868,7 +871,8 @@ } void HTMLInputElement::resetImpl() { - if (m_inputType->storesValueSeparateFromAttribute()) { + if (m_inputType->valueMode() == ValueMode::kValue || + m_inputType->valueMode() == ValueMode::kFilename) { setValue(String()); setNeedsValidityCheck(); }
diff --git a/third_party/WebKit/Source/core/html/forms/BaseButtonInputType.cpp b/third_party/WebKit/Source/core/html/forms/BaseButtonInputType.cpp index 7485a3a8..269a0b1 100644 --- a/third_party/WebKit/Source/core/html/forms/BaseButtonInputType.cpp +++ b/third_party/WebKit/Source/core/html/forms/BaseButtonInputType.cpp
@@ -81,8 +81,8 @@ return new LayoutButton(&element()); } -bool BaseButtonInputType::storesValueSeparateFromAttribute() { - return false; +InputType::ValueMode BaseButtonInputType::valueMode() const { + return ValueMode::kDefault; } void BaseButtonInputType::setValue(const String& sanitizedValue,
diff --git a/third_party/WebKit/Source/core/html/forms/BaseButtonInputType.h b/third_party/WebKit/Source/core/html/forms/BaseButtonInputType.h index 4139d3a..33ea33f 100644 --- a/third_party/WebKit/Source/core/html/forms/BaseButtonInputType.h +++ b/third_party/WebKit/Source/core/html/forms/BaseButtonInputType.h
@@ -55,7 +55,7 @@ bool shouldSaveAndRestoreFormControlState() const override; void appendToFormData(FormData&) const override; LayoutObject* createLayoutObject(const ComputedStyle&) const override; - bool storesValueSeparateFromAttribute() override; + ValueMode valueMode() const override; void setValue(const String&, bool, TextFieldEventBehavior) override; bool matchesDefaultPseudoClass() override;
diff --git a/third_party/WebKit/Source/core/html/forms/BaseCheckableInputType.cpp b/third_party/WebKit/Source/core/html/forms/BaseCheckableInputType.cpp index 5ebffeb..ffea71ee 100644 --- a/third_party/WebKit/Source/core/html/forms/BaseCheckableInputType.cpp +++ b/third_party/WebKit/Source/core/html/forms/BaseCheckableInputType.cpp
@@ -102,8 +102,8 @@ return "on"; } -bool BaseCheckableInputType::storesValueSeparateFromAttribute() { - return false; +InputType::ValueMode BaseCheckableInputType::valueMode() const { + return ValueMode::kDefaultOn; } void BaseCheckableInputType::setValue(const String& sanitizedValue,
diff --git a/third_party/WebKit/Source/core/html/forms/BaseCheckableInputType.h b/third_party/WebKit/Source/core/html/forms/BaseCheckableInputType.h index da85922..ea6c406 100644 --- a/third_party/WebKit/Source/core/html/forms/BaseCheckableInputType.h +++ b/third_party/WebKit/Source/core/html/forms/BaseCheckableInputType.h
@@ -61,7 +61,7 @@ void accessKeyAction(bool sendMouseEvents) final; bool matchesDefaultPseudoClass() override; String fallbackValue() const final; - bool storesValueSeparateFromAttribute() final; + ValueMode valueMode() const override; void setValue(const String&, bool, TextFieldEventBehavior) final; void readingChecked() const final; bool isCheckable() final;
diff --git a/third_party/WebKit/Source/core/html/forms/BaseTemporalInputType.cpp b/third_party/WebKit/Source/core/html/forms/BaseTemporalInputType.cpp index 75e7a19..4167030b 100644 --- a/third_party/WebKit/Source/core/html/forms/BaseTemporalInputType.cpp +++ b/third_party/WebKit/Source/core/html/forms/BaseTemporalInputType.cpp
@@ -59,6 +59,10 @@ return ChooserOnlyTemporalInputTypeView::create(element(), *this); } +InputType::ValueMode BaseTemporalInputType::valueMode() const { + return ValueMode::kValue; +} + double BaseTemporalInputType::valueAsDate() const { return valueAsDouble(); }
diff --git a/third_party/WebKit/Source/core/html/forms/BaseTemporalInputType.h b/third_party/WebKit/Source/core/html/forms/BaseTemporalInputType.h index f9f809c..8d45f3fb 100644 --- a/third_party/WebKit/Source/core/html/forms/BaseTemporalInputType.h +++ b/third_party/WebKit/Source/core/html/forms/BaseTemporalInputType.h
@@ -83,6 +83,7 @@ String badInputText() const override; InputTypeView* createView() override; + ValueMode valueMode() const override; double valueAsDate() const override; void setValueAsDate(double, ExceptionState&) const override; double valueAsDouble() const override;
diff --git a/third_party/WebKit/Source/core/html/forms/ColorInputType.cpp b/third_party/WebKit/Source/core/html/forms/ColorInputType.cpp index 90b15ba3..d70aeaf 100644 --- a/third_party/WebKit/Source/core/html/forms/ColorInputType.cpp +++ b/third_party/WebKit/Source/core/html/forms/ColorInputType.cpp
@@ -93,6 +93,10 @@ return this; } +InputType::ValueMode ColorInputType::valueMode() const { + return ValueMode::kValue; +} + void ColorInputType::countUsage() { countUsageIfVisible(UseCounter::InputTypeColor); }
diff --git a/third_party/WebKit/Source/core/html/forms/ColorInputType.h b/third_party/WebKit/Source/core/html/forms/ColorInputType.h index 96cee6c..1c45676d 100644 --- a/third_party/WebKit/Source/core/html/forms/ColorInputType.h +++ b/third_party/WebKit/Source/core/html/forms/ColorInputType.h
@@ -63,6 +63,7 @@ private: explicit ColorInputType(HTMLInputElement&); InputTypeView* createView() override; + ValueMode valueMode() const override; void valueAttributeChanged() override; void countUsage() override; const AtomicString& formControlType() const override;
diff --git a/third_party/WebKit/Source/core/html/forms/FileInputType.cpp b/third_party/WebKit/Source/core/html/forms/FileInputType.cpp index f234817..0d6b38e 100644 --- a/third_party/WebKit/Source/core/html/forms/FileInputType.cpp +++ b/third_party/WebKit/Source/core/html/forms/FileInputType.cpp
@@ -157,6 +157,10 @@ return new LayoutFileUploadControl(&element()); } +InputType::ValueMode FileInputType::valueMode() const { + return ValueMode::kFilename; +} + bool FileInputType::canSetStringValue() const { return false; }
diff --git a/third_party/WebKit/Source/core/html/forms/FileInputType.h b/third_party/WebKit/Source/core/html/forms/FileInputType.h index bc457045..a5b0c44 100644 --- a/third_party/WebKit/Source/core/html/forms/FileInputType.h +++ b/third_party/WebKit/Source/core/html/forms/FileInputType.h
@@ -76,6 +76,7 @@ bool canSetStringValue() const override; FileList* files() override; void setFiles(FileList*) override; + ValueMode valueMode() const override; bool canSetValue(const String&) override; // Checked first, before internal storage or the value attribute. bool getTypeSpecificValue(String&) override;
diff --git a/third_party/WebKit/Source/core/html/forms/HiddenInputType.cpp b/third_party/WebKit/Source/core/html/forms/HiddenInputType.cpp index 7afda97..d90f2b3d 100644 --- a/third_party/WebKit/Source/core/html/forms/HiddenInputType.cpp +++ b/third_party/WebKit/Source/core/html/forms/HiddenInputType.cpp
@@ -87,8 +87,8 @@ return false; } -bool HiddenInputType::storesValueSeparateFromAttribute() { - return false; +InputType::ValueMode HiddenInputType::valueMode() const { + return ValueMode::kDefault; } void HiddenInputType::setValue(const String& sanitizedValue,
diff --git a/third_party/WebKit/Source/core/html/forms/HiddenInputType.h b/third_party/WebKit/Source/core/html/forms/HiddenInputType.h index 50fd3d3..cad77ca66 100644 --- a/third_party/WebKit/Source/core/html/forms/HiddenInputType.h +++ b/third_party/WebKit/Source/core/html/forms/HiddenInputType.h
@@ -55,7 +55,7 @@ LayoutObject* createLayoutObject(const ComputedStyle&) const override; void accessKeyAction(bool sendMouseEvents) override; bool layoutObjectIsNeeded() override; - bool storesValueSeparateFromAttribute() override; + ValueMode valueMode() const override; bool isInteractiveContent() const override { return false; } bool shouldRespectHeightAndWidthAttributes() override; void setValue(const String&, bool, TextFieldEventBehavior) override;
diff --git a/third_party/WebKit/Source/core/html/forms/InputType.cpp b/third_party/WebKit/Source/core/html/forms/InputType.cpp index d74e3a0..44c21d79 100644 --- a/third_party/WebKit/Source/core/html/forms/InputType.cpp +++ b/third_party/WebKit/Source/core/html/forms/InputType.cpp
@@ -503,10 +503,6 @@ return true; } -bool InputType::storesValueSeparateFromAttribute() { - return true; -} - bool InputType::shouldDispatchFormControlChangeEvent(String& oldValue, String& newValue) { return !equalIgnoringNullity(oldValue, newValue);
diff --git a/third_party/WebKit/Source/core/html/forms/InputType.h b/third_party/WebKit/Source/core/html/forms/InputType.h index 7b6e072..54529a7 100644 --- a/third_party/WebKit/Source/core/html/forms/InputType.h +++ b/third_party/WebKit/Source/core/html/forms/InputType.h
@@ -95,6 +95,10 @@ // is called. virtual String defaultValue() const; + // https://html.spec.whatwg.org/multipage/forms.html#dom-input-value + enum class ValueMode { kValue, kDefault, kDefaultOn, kFilename }; + virtual ValueMode valueMode() const = 0; + virtual double valueAsDate() const; virtual void setValueAsDate(double, ExceptionState&) const; virtual double valueAsDouble() const; @@ -176,7 +180,6 @@ virtual bool canSetSuggestedValue(); virtual bool shouldSendChangeEventAfterCheckedChanged(); virtual bool canSetValue(const String&); - virtual bool storesValueSeparateFromAttribute(); virtual void setValue(const String&, bool valueChanged, TextFieldEventBehavior);
diff --git a/third_party/WebKit/Source/core/html/forms/RangeInputType.cpp b/third_party/WebKit/Source/core/html/forms/RangeInputType.cpp index 5511ee7..9eb7f880 100644 --- a/third_party/WebKit/Source/core/html/forms/RangeInputType.cpp +++ b/third_party/WebKit/Source/core/html/forms/RangeInputType.cpp
@@ -88,6 +88,10 @@ return this; } +InputType::ValueMode RangeInputType::valueMode() const { + return ValueMode::kValue; +} + void RangeInputType::countUsage() { countUsageIfVisible(UseCounter::InputTypeRange); if (const ComputedStyle* style = element().computedStyle()) {
diff --git a/third_party/WebKit/Source/core/html/forms/RangeInputType.h b/third_party/WebKit/Source/core/html/forms/RangeInputType.h index f5a2043b..36cd5d1 100644 --- a/third_party/WebKit/Source/core/html/forms/RangeInputType.h +++ b/third_party/WebKit/Source/core/html/forms/RangeInputType.h
@@ -50,6 +50,7 @@ private: RangeInputType(HTMLInputElement&); InputTypeView* createView() override; + ValueMode valueMode() const override; void countUsage() override; const AtomicString& formControlType() const override; double valueAsDouble() const override;
diff --git a/third_party/WebKit/Source/core/html/forms/TextFieldInputType.cpp b/third_party/WebKit/Source/core/html/forms/TextFieldInputType.cpp index 0dda4f82..410a938a 100644 --- a/third_party/WebKit/Source/core/html/forms/TextFieldInputType.cpp +++ b/third_party/WebKit/Source/core/html/forms/TextFieldInputType.cpp
@@ -120,6 +120,10 @@ return this; } +InputType::ValueMode TextFieldInputType::valueMode() const { + return ValueMode::kValue; +} + SpinButtonElement* TextFieldInputType::spinButtonElement() const { return toSpinButtonElement(element().userAgentShadowRoot()->getElementById( ShadowElementNames::spinButton()));
diff --git a/third_party/WebKit/Source/core/html/forms/TextFieldInputType.h b/third_party/WebKit/Source/core/html/forms/TextFieldInputType.h index 3a5441e..fe0c91ff 100644 --- a/third_party/WebKit/Source/core/html/forms/TextFieldInputType.h +++ b/third_party/WebKit/Source/core/html/forms/TextFieldInputType.h
@@ -82,6 +82,7 @@ private: InputTypeView* createView() override; + ValueMode valueMode() const override; bool shouldShowFocusRingOnMouseFocus() const final; bool isTextField() const final; bool valueMissing(const String&) const override;
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.h b/third_party/WebKit/Source/core/layout/LayoutBox.h index 5b7bb32..ee2fb8f 100644 --- a/third_party/WebKit/Source/core/layout/LayoutBox.h +++ b/third_party/WebKit/Source/core/layout/LayoutBox.h
@@ -1051,7 +1051,7 @@ AvoidBreaks // Preferably avoid breaks. If not possible, examine children // to find possible break points. }; - PaginationBreakability getPaginationBreakability() const; + virtual PaginationBreakability getPaginationBreakability() const; LayoutRect localCaretRect( InlineBox*,
diff --git a/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.cpp b/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.cpp index cf82704..7b0ccd3 100644 --- a/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.cpp
@@ -447,13 +447,12 @@ } void LayoutFlexibleBox::repositionLogicalHeightDependentFlexItems( - Vector<LineContext>& lineContexts, - LayoutObject* childToExclude) { + Vector<LineContext>& lineContexts) { LayoutUnit crossAxisStartEdge = lineContexts.isEmpty() ? LayoutUnit() : lineContexts[0].crossAxisOffset; alignFlexLines(lineContexts); - alignChildren(lineContexts, childToExclude); + alignChildren(lineContexts); if (style()->flexWrap() == FlexWrapReverse) flipForWrapReverse(lineContexts, crossAxisStartEdge); @@ -1061,7 +1060,7 @@ } updateLogicalHeight(); - repositionLogicalHeightDependentFlexItems(lineContexts, childToExclude); + repositionLogicalHeightDependentFlexItems(lineContexts); } LayoutUnit LayoutFlexibleBox::autoMarginOffsetInMainAxis( @@ -2133,8 +2132,7 @@ LayoutSize(LayoutUnit(), delta)); } -void LayoutFlexibleBox::alignChildren(const Vector<LineContext>& lineContexts, - LayoutObject* childToExclude) { +void LayoutFlexibleBox::alignChildren(const Vector<LineContext>& lineContexts) { // Keep track of the space between the baseline edge and the after edge of // the box for each line. Vector<LayoutUnit> minMarginAfterBaselines; @@ -2149,8 +2147,6 @@ for (size_t childNumber = 0; childNumber < lineContext.flexItems.size(); ++childNumber) { const FlexItem& flexItem = lineContext.flexItems[childNumber]; - if (flexItem.box == childToExclude) - continue; if (flexItem.box->isOutOfFlowPositioned()) { continue; }
diff --git a/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.h b/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.h index f8cf55c..0abca82 100644 --- a/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.h +++ b/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.h
@@ -179,8 +179,7 @@ bool hasAutoMarginsInCrossAxis(const LayoutBox& child) const; bool updateAutoMarginsInCrossAxis(LayoutBox& child, LayoutUnit availableAlignmentSpace); - void repositionLogicalHeightDependentFlexItems(Vector<LineContext>&, - LayoutObject* childToExclude); + void repositionLogicalHeightDependentFlexItems(Vector<LineContext>&); LayoutUnit clientLogicalBottomAfterRepositioning(); LayoutUnit availableAlignmentSpaceForChild(LayoutUnit lineCrossAxisExtent, @@ -243,7 +242,7 @@ LayoutUnit crossAxisOffset, LayoutUnit availableFreeSpace); void alignFlexLines(Vector<LineContext>&); - void alignChildren(const Vector<LineContext>&, LayoutObject* childToExclude); + void alignChildren(const Vector<LineContext>&); void applyStretchAlignmentToChild(LayoutBox& child, LayoutUnit lineCrossAxisExtent); void flipForRightToLeftColumn(const Vector<LineContext>& lineContexts);
diff --git a/third_party/WebKit/Source/core/layout/LayoutListMarker.h b/third_party/WebKit/Source/core/layout/LayoutListMarker.h index 150f0bb..fe87edde 100644 --- a/third_party/WebKit/Source/core/layout/LayoutListMarker.h +++ b/third_party/WebKit/Source/core/layout/LayoutListMarker.h
@@ -105,6 +105,10 @@ void styleDidChange(StyleDifference, const ComputedStyle* oldStyle) override; bool anonymousHasStylePropagationOverride() override { return true; } + bool paintedOutputOfObjectHasNoEffectRegardlessOfSize() const override { + return false; + } + String m_text; Persistent<StyleImage> m_image; LayoutListItem* m_listItem;
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp b/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp index 8215fdb..38214df 100644 --- a/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp
@@ -195,6 +195,7 @@ for (LayoutTableCell* cell = firstCell(); cell; cell = cell->nextCell()) { SubtreeLayoutScope layouter(*cell); + cell->setLogicalTop(logicalTop()); if (!cell->needsLayout()) markChildForPaginationRelayoutIfNeeded(*cell, layouter); if (cell->needsLayout()) @@ -258,6 +259,19 @@ return false; } +LayoutBox::PaginationBreakability LayoutTableRow::getPaginationBreakability() + const { + PaginationBreakability breakability = + LayoutTableBoxComponent::getPaginationBreakability(); + if (breakability == AllowAnyBreaks) { + // Even if the row allows us to break inside, we will want to prevent that + // if we have a header group that wants to appear at the top of each page. + if (const LayoutTableSection* header = table()->header()) + breakability = header->getPaginationBreakability(); + } + return breakability; +} + void LayoutTableRow::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) const { TableRowPainter(*this).paint(paintInfo, paintOffset);
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableRow.h b/third_party/WebKit/Source/core/layout/LayoutTableRow.h index a8999349..5307990 100644 --- a/third_party/WebKit/Source/core/layout/LayoutTableRow.h +++ b/third_party/WebKit/Source/core/layout/LayoutTableRow.h
@@ -123,6 +123,8 @@ const LayoutPoint& accumulatedOffset, HitTestAction) override; + PaginationBreakability getPaginationBreakability() const final; + void computeOverflow(); const char* name() const override { return "LayoutTableRow"; }
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp b/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp index f4681d9..fc9f44e 100644 --- a/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp
@@ -943,6 +943,7 @@ LayoutState state(*this, locationOffset()); const Vector<int>& columnPos = table()->effectiveColumnPositions(); + LayoutUnit rowLogicalTop; SubtreeLayoutScope layouter(*this); for (unsigned r = 0; r < m_grid.size(); ++r) { @@ -970,9 +971,18 @@ } if (LayoutTableRow* rowLayoutObject = m_grid[r].rowLayoutObject) { - if (!rowLayoutObject->needsLayout()) - markChildForPaginationRelayoutIfNeeded(*rowLayoutObject, layouter); + if (state.isPaginated()) { + rowLayoutObject->setLogicalTop(rowLogicalTop); + if (!rowLayoutObject->needsLayout()) + markChildForPaginationRelayoutIfNeeded(*rowLayoutObject, layouter); + } rowLayoutObject->layoutIfNeeded(); + if (state.isPaginated()) { + rowLayoutObject->setLogicalHeight( + LayoutUnit(logicalHeightForRow(*rowLayoutObject))); + rowLogicalTop = rowLayoutObject->logicalBottom(); + rowLogicalTop += LayoutUnit(table()->vBorderSpacing()); + } } } @@ -1253,15 +1263,7 @@ int LayoutTableSection::paginationStrutForRow(LayoutTableRow* row, LayoutUnit logicalOffset) const { DCHECK(row); - // Even if the row allows us to break-inside, we will want to put a strut on - // the row if we have a header group that wants to appear at the top of each - // page. - bool tableHeaderForcesStrut = - table()->header() - ? table()->header()->getPaginationBreakability() != AllowAnyBreaks - : false; - if (row->getPaginationBreakability() == AllowAnyBreaks && - !tableHeaderForcesStrut) + if (row->getPaginationBreakability() == AllowAnyBreaks) return 0; LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset); if (!pageLogicalHeight) @@ -1975,6 +1977,27 @@ } } +int LayoutTableSection::logicalHeightForRow( + const LayoutTableRow& rowObject) const { + unsigned rowIndex = rowObject.rowIndex(); + int logicalHeight = 0; + const Row& row = m_grid[rowIndex].row; + unsigned cols = row.size(); + for (unsigned colIndex = 0; colIndex < cols; colIndex++) { + const CellStruct& cellStruct = cellAt(rowIndex, colIndex); + const LayoutTableCell* cell = cellStruct.primaryCell(); + if (!cell || cellStruct.inColSpan) + continue; + // TODO(mstensho): Rowspanned cells also need to contribute to row heights + // during the first layout pass, in order to get fragmentation right. + if (cell->rowSpan() == 1) { + logicalHeight = + std::max(logicalHeight, cell->logicalHeightForRowSizing()); + } + } + return logicalHeight; +} + bool LayoutTableSection::isRepeatingHeaderGroup() const { if (getPaginationBreakability() == LayoutBox::AllowAnyBreaks) return false;
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableSection.h b/third_party/WebKit/Source/core/layout/LayoutTableSection.h index 650af9f..dd91b95c1 100644 --- a/third_party/WebKit/Source/core/layout/LayoutTableSection.h +++ b/third_party/WebKit/Source/core/layout/LayoutTableSection.h
@@ -419,6 +419,8 @@ void relayoutCellIfFlexed(LayoutTableCell&, int rowIndex, int rowHeight); + int logicalHeightForRow(const LayoutTableRow&) const; + // The representation of the rows and their cells (CellStruct). Vector<RowStruct> m_grid;
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc index ac0af34d..83dc704 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc +++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
@@ -24,6 +24,41 @@ curr_margin_strut.negative_margin_block_start.abs()); } +// Whether an in-flow block-level child creates a new formatting context. +// +// This will *NOT* check the following cases: +// - The child is out-of-flow, e.g. floating or abs-pos. +// - The child is a inline-level, e.g. "display: inline-block". +// - The child establishes a new formatting context, but should be a child of +// another layout algorithm, e.g. "display: table-caption" or flex-item. +bool IsNewFormattingContextForInFlowBlockLevelChild( + const NGConstraintSpace& space, + const ComputedStyle& style) { + // TODO(layout-dev): This doesn't capture a few cases which can't be computed + // directly from style yet: + // - The child is a <fieldset>. + // - "column-span: all" is set on the child (requires knowledge that we are + // in a multi-col formatting context). + // (https://drafts.csswg.org/css-multicol-1/#valdef-column-span-all) + + if (style.specifiesColumns() || style.containsPaint() || + style.containsLayout()) + return true; + + if (!style.isOverflowVisible()) + return true; + + EDisplay display = style.display(); + if (display == EDisplay::Grid || display == EDisplay::Flex || + display == EDisplay::Box) + return true; + + if (space.WritingMode() != FromPlatformWritingMode(style.getWritingMode())) + return true; + + return false; +} + } // namespace NGBlockLayoutAlgorithm::NGBlockLayoutAlgorithm( @@ -73,6 +108,10 @@ } case kStateChildLayout: { if (current_child_) { + constraint_space_for_children_->SetIsNewFormattingContext( + IsNewFormattingContextForInFlowBlockLevelChild( + *constraint_space, *current_child_->Style())); + NGFragment* fragment; if (!current_child_->Layout(constraint_space_for_children_, &fragment)) return false;
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc index 3e5ac59..35c6bb4 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc +++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc
@@ -163,7 +163,8 @@ EXPECT_TRUE(frag->MarginStrut().IsEmpty()); ASSERT_EQ(frag->Children().size(), 1UL); - const NGPhysicalFragmentBase* div2_fragment = frag->Children()[0]; + const NGPhysicalFragment* div2_fragment = + static_cast<const NGPhysicalFragment*>(frag->Children()[0].get()); EXPECT_EQ(NGMarginStrut({LayoutUnit(kDiv2MarginTop)}), div2_fragment->MarginStrut()); EXPECT_EQ(kDiv1MarginTop, div2_fragment->TopOffset()); @@ -339,7 +340,8 @@ ASSERT_EQ(frag->Children().size(), 1UL); EXPECT_EQ(NGMarginStrut({LayoutUnit(kDiv2Margin), LayoutUnit(kDiv2Margin)}), - frag->Children()[0]->MarginStrut()); + static_cast<const NGPhysicalFragment*>(frag->Children()[0].get()) + ->MarginStrut()); // Reset padding and verify that margins DO collapse. div1_style->setPaddingTop(Length(0, Fixed)); @@ -394,6 +396,67 @@ EXPECT_EQ(kVerticalDivWidth + kHorizontalDivMarginLeft, child->LeftOffset()); } +// Verifies that the margin strut of a child with a different writing mode does +// not get used in the collapsing margins calculation. +// +// Test case's HTML representation: +// <style> +// #div1 { margin-bottom: 10px; height: 60px; writing-mode: vertical-rl; } +// #div2 { margin-left: -20px; width: 10px; } +// #div3 { margin-top: 40px; height: 60px; } +// </style> +// <div id="div1"> +// <div id="div2">vertical</div> +// </div> +// <div id="div3"></div> +TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase6) { + const int kHeight = 60; + const int kWidth = 10; + const int kMarginBottom = 10; + const int kMarginLeft = -20; + const int kMarginTop = 40; + + style_->setWidth(Length(500, Fixed)); + style_->setHeight(Length(500, Fixed)); + + // DIV1 + RefPtr<ComputedStyle> div1_style = ComputedStyle::create(); + div1_style->setWidth(Length(kWidth, Fixed)); + div1_style->setHeight(Length(kHeight, Fixed)); + div1_style->setWritingMode(RightToLeftWritingMode); + div1_style->setMarginBottom(Length(kMarginBottom, Fixed)); + NGBox* div1 = new NGBox(div1_style.get()); + + // DIV2 + RefPtr<ComputedStyle> div2_style = ComputedStyle::create(); + div2_style->setWidth(Length(kWidth, Fixed)); + div2_style->setMarginLeft(Length(kMarginLeft, Fixed)); + NGBox* div2 = new NGBox(div2_style.get()); + + // DIV3 + RefPtr<ComputedStyle> div3_style = ComputedStyle::create(); + div3_style->setHeight(Length(kHeight, Fixed)); + div3_style->setMarginTop(Length(kMarginTop, Fixed)); + NGBox* div3 = new NGBox(div3_style.get()); + + div1->SetFirstChild(div2); + div1->SetNextSibling(div3); + + auto* space = + new NGConstraintSpace(HorizontalTopBottom, LeftToRight, + NGLogicalSize(LayoutUnit(500), LayoutUnit(500))); + NGPhysicalFragment* frag = RunBlockLayoutAlgorithm(space, div1); + + ASSERT_EQ(frag->Children().size(), 2UL); + + const NGPhysicalFragmentBase* child1 = frag->Children()[0]; + EXPECT_EQ(0, child1->TopOffset()); + EXPECT_EQ(kHeight, child1->Height()); + + const NGPhysicalFragmentBase* child2 = frag->Children()[1]; + EXPECT_EQ(kHeight + std::max(kMarginBottom, kMarginTop), child2->TopOffset()); +} + // Verifies that a box's size includes its borders and padding, and that // children are positioned inside the content box. //
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_box.cc b/third_party/WebKit/Source/core/layout/ng/ng_box.cc index e7a4f4e6..cf75f73 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_box.cc +++ b/third_party/WebKit/Source/core/layout/ng/ng_box.cc
@@ -15,13 +15,6 @@ #include "core/layout/ng/ng_writing_mode.h" namespace blink { -namespace { -// TODO(layout-ng): Add m_isNewFc flag to ComputedStyle and set it from -// StyleResolver instead of getting it calculated by createsNewFormattingContext -bool IsNewFormattingContext(const LayoutBox* layout_box) { - return layout_box && toLayoutBlock(layout_box)->createsNewFormattingContext(); -} -} // namespace NGBox::NGBox(LayoutObject* layout_object) : layout_box_(toLayoutBox(layout_object)) { @@ -46,8 +39,6 @@ NGConstraintSpace* child_constraint_space = new NGConstraintSpace( FromPlatformWritingMode(Style()->getWritingMode()), FromPlatformDirection(Style()->direction()), constraint_space); - child_constraint_space->SetIsNewFormattingContext( - IsNewFormattingContext(layout_box_)); NGPhysicalFragment* fragment = nullptr; if (!algorithm_->Layout(child_constraint_space, &fragment))
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_constraint_space.cc b/third_party/WebKit/Source/core/layout/ng/ng_constraint_space.cc index e2292d2..460f4372f 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_constraint_space.cc +++ b/third_party/WebKit/Source/core/layout/ng/ng_constraint_space.cc
@@ -18,8 +18,7 @@ container_size.ConvertToPhysical(writing_mode))), size_(container_size), writing_mode_(writing_mode), - direction_(direction), - is_new_fc_(false) {} + direction_(direction) {} NGConstraintSpace::NGConstraintSpace(NGWritingMode writing_mode, NGDirection direction, @@ -27,8 +26,7 @@ : physical_space_(physical_space), size_(physical_space->ContainerSize().ConvertToLogical(writing_mode)), writing_mode_(writing_mode), - direction_(direction), - is_new_fc_(false) {} + direction_(direction) {} NGConstraintSpace::NGConstraintSpace(NGWritingMode writing_mode, NGDirection direction, @@ -37,8 +35,7 @@ offset_(constraint_space->Offset()), size_(constraint_space->Size()), writing_mode_(writing_mode), - direction_(direction), - is_new_fc_(false) {} + direction_(direction) {} NGConstraintSpace::NGConstraintSpace(const NGConstraintSpace& other, NGLogicalOffset offset, @@ -47,17 +44,13 @@ offset_(offset), size_(size), writing_mode_(other.WritingMode()), - direction_(other.Direction()), - is_new_fc_(false) {} + direction_(other.Direction()) {} NGConstraintSpace::NGConstraintSpace(NGWritingMode writing_mode, NGDirection direction, const NGConstraintSpace& other, NGLogicalSize size) - : size_(size), - writing_mode_(writing_mode), - direction_(direction), - is_new_fc_(false) { + : size_(size), writing_mode_(writing_mode), direction_(direction) { physical_space_ = new NGPhysicalConstraintSpace(size.ConvertToPhysical(writing_mode)); for (const auto& exclusion : other.PhysicalSpace()->Exclusions()) { @@ -67,7 +60,7 @@ NGConstraintSpace* NGConstraintSpace::CreateFromLayoutObject( const LayoutBox& box) { - bool fixed_inline = false, fixed_block = false; + bool fixed_inline = false, fixed_block = false, is_new_fc = false; // XXX for orthogonal writing mode this is not right LayoutUnit container_logical_width = std::max(LayoutUnit(), box.containingBlockLogicalWidthForContent()); @@ -92,6 +85,9 @@ fixed_block = true; } + if (box.isLayoutBlock() && toLayoutBlock(box).createsNewFormattingContext()) + is_new_fc = true; + NGConstraintSpace* derived_constraint_space = new NGConstraintSpace( FromPlatformWritingMode(box.styleRef().getWritingMode()), FromPlatformDirection(box.styleRef().direction()), @@ -100,6 +96,8 @@ box.styleRef().overflowInlineDirection() == OverflowAuto, box.styleRef().overflowBlockDirection() == OverflowAuto); derived_constraint_space->SetFixedSize(fixed_inline, fixed_block); + derived_constraint_space->SetIsNewFormattingContext(is_new_fc); + return derived_constraint_space; } @@ -108,6 +106,10 @@ static_cast<NGWritingMode>(writing_mode_)); } +bool NGConstraintSpace::IsNewFormattingContext() const { + return physical_space_->is_new_fc_; +} + bool NGConstraintSpace::InlineTriggersScrollbar() const { return writing_mode_ == HorizontalTopBottom ? physical_space_->width_direction_triggers_scrollbar_ @@ -184,6 +186,10 @@ } } +void NGConstraintSpace::SetIsNewFormattingContext(bool is_new_fc) { + physical_space_->is_new_fc_ = is_new_fc; +} + String NGConstraintSpace::ToString() const { return String::format("%s,%s %sx%s", offset_.inline_offset.toString().ascii().data(),
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_constraint_space.h b/third_party/WebKit/Source/core/layout/ng/ng_constraint_space.h index 6454993f..131dbb9 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_constraint_space.h +++ b/third_party/WebKit/Source/core/layout/ng/ng_constraint_space.h
@@ -87,7 +87,7 @@ // Whether the current constraint space is for the newly established // Formatting Context. - bool IsNewFormattingContext() const { return is_new_fc_; } + bool IsNewFormattingContext() const; // Whether exceeding the containerSize triggers the presence of a scrollbar // for the indicated direction. @@ -123,9 +123,7 @@ void SetOverflowTriggersScrollbar(bool inlineTriggers, bool blockTriggers); void SetFixedSize(bool inlineFixed, bool blockFixed); void SetFragmentationType(NGFragmentationType); - // TODO(layout-ng): Add m_isNewFc flag to ComputedStyle and use it instead of - // the function below. - void SetIsNewFormattingContext(bool is_new_fc) { is_new_fc_ = is_new_fc; } + void SetIsNewFormattingContext(bool is_new_fc); String ToString() const; @@ -135,9 +133,6 @@ NGLogicalSize size_; unsigned writing_mode_ : 3; unsigned direction_ : 1; - // Whether the current constraint space is for the newly established - // formatting Context - bool is_new_fc_ : 1; }; inline std::ostream& operator<<(std::ostream& stream,
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment.cc b/third_party/WebKit/Source/core/layout/ng/ng_fragment.cc index 4085e38..ca9dcb63 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_fragment.cc +++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment.cc
@@ -4,4 +4,15 @@ #include "core/layout/ng/ng_fragment.h" -namespace blink {} // namespace blink +#include "core/layout/ng/ng_physical_fragment.h" + +namespace blink { + +NGMarginStrut NGFragment::MarginStrut() const { + // NOTE: Accessing the margin strut ignoring the writing mode here is fine. + // Changing the writing mode establishes a new formatting context, for which + // a margin strut is never set for a fragment. + return toNGPhysicalFragment(physical_fragment_)->MarginStrut(); +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment.h b/third_party/WebKit/Source/core/layout/ng/ng_fragment.h index 404b3b85..ef2b32d0 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_fragment.h +++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment.h
@@ -23,6 +23,8 @@ NGDirection direction, NGPhysicalFragment* physical_fragment) : NGFragmentBase(writing_mode, direction, physical_fragment) {} + + NGMarginStrut MarginStrut() const; }; } // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment_base.cc b/third_party/WebKit/Source/core/layout/ng/ng_fragment_base.cc index 14ea494..0193580f 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_fragment_base.cc +++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment_base.cc
@@ -41,11 +41,6 @@ : physical_fragment_->LeftOffset(); } -NGMarginStrut NGFragmentBase::MarginStrut() const { - // TODO(layout-ng): Change MarginStrut to support writing direction. - return physical_fragment_->MarginStrut(); -} - DEFINE_TRACE(NGFragmentBase) { visitor->trace(physical_fragment_); }
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment_base.h b/third_party/WebKit/Source/core/layout/ng/ng_fragment_base.h index 9e4ada8..48b4948 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_fragment_base.h +++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment_base.h
@@ -36,8 +36,6 @@ LayoutUnit InlineOffset() const; LayoutUnit BlockOffset() const; - NGMarginStrut MarginStrut() const; - NGPhysicalFragmentBase* PhysicalFragment() const { return physical_fragment_; };
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_physical_constraint_space.h b/third_party/WebKit/Source/core/layout/ng/ng_physical_constraint_space.h index de9b943..b47d5d4 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_physical_constraint_space.h +++ b/third_party/WebKit/Source/core/layout/ng/ng_physical_constraint_space.h
@@ -83,6 +83,10 @@ unsigned width_direction_fragmentation_type_ : 2; unsigned height_direction_fragmentation_type_ : 2; + // Whether the current constraint space is for the newly established + // formatting Context + unsigned is_new_fc_ : 1; + HeapVector<Member<const NGExclusion>> exclusions_; };
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_physical_fragment.h b/third_party/WebKit/Source/core/layout/ng/ng_physical_fragment.h index 28ad7cb8..4a77cde1 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_physical_fragment.h +++ b/third_party/WebKit/Source/core/layout/ng/ng_physical_fragment.h
@@ -20,7 +20,8 @@ NGPhysicalSize overflow, HeapVector<Member<const NGPhysicalFragmentBase>>& children, NGMarginStrut margin_strut) - : NGPhysicalFragmentBase(size, overflow, FragmentBox, margin_strut) { + : NGPhysicalFragmentBase(size, overflow, FragmentBox), + margin_strut_(margin_strut) { children_.swap(children); } @@ -28,6 +29,8 @@ return children_; } + NGMarginStrut MarginStrut() const { return margin_strut_; } + DEFINE_INLINE_TRACE_AFTER_DISPATCH() { visitor->trace(children_); NGPhysicalFragmentBase::traceAfterDispatch(visitor); @@ -35,10 +38,18 @@ private: HeapVector<Member<const NGPhysicalFragmentBase>> children_; + + NGMarginStrut margin_strut_; }; WILL_NOT_BE_EAGERLY_TRACED_CLASS(NGPhysicalFragment); +DEFINE_TYPE_CASTS(NGPhysicalFragment, + NGPhysicalFragmentBase, + fragment, + fragment->Type() == NGPhysicalFragmentBase::FragmentBox, + fragment.Type() == NGPhysicalFragmentBase::FragmentBox); + } // namespace blink #endif // NGPhysicalFragment_h
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_physical_fragment_base.h b/third_party/WebKit/Source/core/layout/ng/ng_physical_fragment_base.h index 0a5000a..75a22999 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_physical_fragment_base.h +++ b/third_party/WebKit/Source/core/layout/ng/ng_physical_fragment_base.h
@@ -59,26 +59,21 @@ has_been_placed_ = true; } - NGMarginStrut MarginStrut() const { return margin_strut_; } - DEFINE_INLINE_TRACE_AFTER_DISPATCH() {} DECLARE_TRACE(); protected: NGPhysicalFragmentBase(NGPhysicalSize size, NGPhysicalSize overflow, - NGFragmentType type, - NGMarginStrut margin_strut) + NGFragmentType type) : size_(size), overflow_(overflow), - margin_strut_(margin_strut), type_(type), has_been_placed_(false) {} NGPhysicalSize size_; NGPhysicalSize overflow_; NGPhysicalOffset offset_; - NGMarginStrut margin_strut_; unsigned type_ : 1; unsigned has_been_placed_ : 1;
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_physical_text_fragment.h b/third_party/WebKit/Source/core/layout/ng/ng_physical_text_fragment.h index 5495c46..23d05c3 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_physical_text_fragment.h +++ b/third_party/WebKit/Source/core/layout/ng/ng_physical_text_fragment.h
@@ -17,7 +17,7 @@ class CORE_EXPORT NGPhysicalTextFragment final : public NGPhysicalFragmentBase { public: NGPhysicalTextFragment(NGPhysicalSize size, NGPhysicalSize overflow) - : NGPhysicalFragmentBase(size, overflow, FragmentText, {}) {} + : NGPhysicalFragmentBase(size, overflow, FragmentText) {} String Text() const { return text_list_->Text(start_offset_, end_offset_); }
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp b/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp index 9b59bc34a..92ba5a9 100644 --- a/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp +++ b/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp
@@ -441,9 +441,6 @@ PropertyTreeState propertyTreeState = properties->localBorderBoxProperties()->propertyTreeState; - if (properties->cssClip()) - propertyTreeState.setClip(properties->cssClip()); - if (isForeground && shouldClipOverflow(context) && properties->overflowClip()) propertyTreeState.setClip(properties->overflowClip());
diff --git a/third_party/WebKit/Source/devtools/.gitignore b/third_party/WebKit/Source/devtools/.gitignore index f9757b2c..05a1d64b 100644 --- a/third_party/WebKit/Source/devtools/.gitignore +++ b/third_party/WebKit/Source/devtools/.gitignore
@@ -9,5 +9,6 @@ node_modules config.gypi npm-debug.log +/.test_cache /release /scripts/local_node/runtimes
diff --git a/third_party/WebKit/Source/devtools/BUILD.gn b/third_party/WebKit/Source/devtools/BUILD.gn index 44e01495..0325eb4 100644 --- a/third_party/WebKit/Source/devtools/BUILD.gn +++ b/third_party/WebKit/Source/devtools/BUILD.gn
@@ -101,6 +101,7 @@ ] devtools_sdk_js_files = [ "front_end/sdk/ApplicationCacheModel.js", + "front_end/sdk/Connections.js", "front_end/sdk/ConsoleModel.js", "front_end/sdk/ContentProviders.js", "front_end/sdk/CookieParser.js", @@ -295,7 +296,6 @@ "front_end/main/remoteDebuggingTerminatedScreen.css", "front_end/main/renderingOptions.css", "front_end/main/targetCrashedScreen.css", - "front_end/main/Connections.js", "front_end/main/Main.js", "front_end/main/OverlayController.js", "front_end/main/RemoteLocationManager.js",
diff --git a/third_party/WebKit/Source/devtools/front_end/audits2/Audits2Panel.js b/third_party/WebKit/Source/devtools/front_end/audits2/Audits2Panel.js index e6ab2af..d4026c0 100644 --- a/third_party/WebKit/Source/devtools/front_end/audits2/Audits2Panel.js +++ b/third_party/WebKit/Source/devtools/front_end/audits2/Audits2Panel.js
@@ -17,22 +17,44 @@ WebInspector.Audits2Panel.prototype = { _start: function() { - this._backend().then(backend => backend ? backend.send("start") : undefined).then(console.error.bind(console, "STARTED")); + WebInspector.targetManager.interceptMainConnection(this._dispatchProtocolMessage.bind(this)).then(rawConnection => { + this._rawConnection = rawConnection; + this._send("start"); + }); + }, + + /** + * @param {string} message + */ + _dispatchProtocolMessage: function(message) + { + this._send("dispatchProtocolMessage", {message: message}); }, _stop: function() { - this._backend().then(backend => backend ? backend.send("stop") : undefined).then(console.error.bind(console, "STOPPED")); + this._send("stop").then(() => { + this._rawConnection.yieldConnection(); + this._backend.dispose(); + delete this._backend; + delete this._backendPromise; + }); }, /** - * @return {!Promise<?WebInspector.ServiceManager.Service>} + * @param {string} method + * @param {!Object=} params + * @return {!Promise<!Object|undefined>} */ - _backend: function() + _send: function(method, params) { - if (!this._backendPromise) - this._backendPromise = WebInspector.serviceManager.createAppService("audits2_worker", "Audits2Service", false); - return this._backendPromise; + if (!this._backendPromise) { + this._backendPromise = WebInspector.serviceManager.createAppService("audits2_worker", "Audits2Service", false).then(backend => { + this._backend = backend; + this._backend.on("sendProtocolMessage", result => this._rawConnection.send(result.message)); + }); + } + return this._backendPromise.then(() => this._backend ? this._backend.send(method, params) : undefined); }, __proto__: WebInspector.Panel.prototype
diff --git a/third_party/WebKit/Source/devtools/front_end/audits2_worker/Audits2Service.js b/third_party/WebKit/Source/devtools/front_end/audits2_worker/Audits2Service.js index 957d7a1..29e0293 100644 --- a/third_party/WebKit/Source/devtools/front_end/audits2_worker/Audits2Service.js +++ b/third_party/WebKit/Source/devtools/front_end/audits2_worker/Audits2Service.js
@@ -6,8 +6,9 @@ * @constructor * @implements {Service} */ -function Audits2Service() +function Audits2Service(notify) { + this._notify = notify; } Audits2Service.prototype = { @@ -16,7 +17,10 @@ */ start: function() { - console.error("WORKER START"); + console.error("************ WORKER START *****************"); + this._notify("sendProtocolMessage", {message: JSON.stringify({id: 1, method: "Page.enable"})}); + this._notify("sendProtocolMessage", {message: JSON.stringify({id: 2, method: "Runtime.enable"})}); + this._notify("sendProtocolMessage", {message: JSON.stringify({id: 3, method: "Page.reload"})}); return Promise.resolve(); }, @@ -25,16 +29,28 @@ */ stop: function() { - console.error("WORKER STOP"); + console.error("************ WORKER STOP *****************"); + return Promise.resolve(); + }, + + /** + * @param {!Object=} params + * @return {!Promise} + */ + dispatchProtocolMessage: function(params) + { + console.error("message: " + JSON.stringify(params)); return Promise.resolve(); }, /** * @override + * @return {!Promise} */ dispose: function() { - console.error("WORKER DISPOSE"); + console.error("************ WORKER DISPOSE *****************"); + return Promise.resolve(); } }
diff --git a/third_party/WebKit/Source/devtools/front_end/devtools.js b/third_party/WebKit/Source/devtools/front_end/devtools.js index 1ef19d9..e947c6b 100644 --- a/third_party/WebKit/Source/devtools/front_end/devtools.js +++ b/third_party/WebKit/Source/devtools/front_end/devtools.js
@@ -691,6 +691,15 @@ /** * @override + * @param {function()} callback + */ + reattach: function(callback) + { + DevToolsAPI.sendMessageToEmbedder("reattach", [], callback); + }, + + /** + * @override */ readyForTest: function() {
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js b/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js index 988fa17e..d7f640d 100644 --- a/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js +++ b/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
@@ -2918,7 +2918,7 @@ break; case "Enter": // Accept any available autocompletions and advance to the next field. - if (this.autoCompleteElement && this.autoCompleteElement.textContent.length) { + if (this.text() !== this.userEnteredText()) { this.tabKeyPressed(); return; }
diff --git a/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHost.js b/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHost.js index 0e5d496..551509c 100644 --- a/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHost.js +++ b/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHost.js
@@ -391,6 +391,14 @@ /** * @override + * @param {function()} callback + */ + reattach: function(callback) + { + }, + + /** + * @override */ readyForTest: function() {
diff --git a/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHostAPI.js b/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHostAPI.js index cf9cb9e..c9b6f1f0 100644 --- a/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHostAPI.js +++ b/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHostAPI.js
@@ -290,6 +290,11 @@ showContextMenuAtPoint: function(x, y, items, document) { }, /** + * @param {function()} callback + */ + reattach: function(callback) { }, + + /** * @return {boolean} */ isUnderTest: function() { },
diff --git a/third_party/WebKit/Source/devtools/front_end/main/Connections.js b/third_party/WebKit/Source/devtools/front_end/main/Connections.js deleted file mode 100644 index fbab8df5..0000000 --- a/third_party/WebKit/Source/devtools/front_end/main/Connections.js +++ /dev/null
@@ -1,143 +0,0 @@ -// Copyright (c) 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @constructor - * @extends {InspectorBackendClass.Connection} - */ -WebInspector.MainConnection = function() -{ - InspectorBackendClass.Connection.call(this); - InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.DispatchMessage, this._dispatchMessage, this); - InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.DispatchMessageChunk, this._dispatchMessageChunk, this); -} - -WebInspector.MainConnection.prototype = { - /** - * @override - * @param {!Object} messageObject - */ - sendMessage: function(messageObject) - { - var message = JSON.stringify(messageObject); - InspectorFrontendHost.sendMessageToBackend(message); - }, - - /** - * @param {!WebInspector.Event} event - */ - _dispatchMessage: function(event) - { - this.dispatch(/** @type {string} */ (event.data)); - }, - - /** - * @param {!WebInspector.Event} event - */ - _dispatchMessageChunk: function(event) - { - var messageChunk = /** @type {string} */ (event.data["messageChunk"]); - var messageSize = /** @type {number} */ (event.data["messageSize"]); - if (messageSize) { - this._messageBuffer = ""; - this._messageSize = messageSize; - } - this._messageBuffer += messageChunk; - if (this._messageBuffer.length === this._messageSize) { - this.dispatch(this._messageBuffer); - this._messageBuffer = ""; - this._messageSize = 0; - } - }, - - __proto__: InspectorBackendClass.Connection.prototype -} - -/** - * @constructor - * @extends {InspectorBackendClass.Connection} - * @param {string} url - * @param {function(!InspectorBackendClass.Connection)} onConnectionReady - */ -WebInspector.WebSocketConnection = function(url, onConnectionReady) -{ - InspectorBackendClass.Connection.call(this); - this._socket = new WebSocket(url); - this._socket.onmessage = this._onMessage.bind(this); - this._socket.onerror = this._onError.bind(this); - this._socket.onopen = onConnectionReady.bind(null, this); - this._socket.onclose = this.connectionClosed.bind(this, "websocket_closed"); -} - -/** - * @param {string} url - * @param {function(!InspectorBackendClass.Connection)} onConnectionReady - */ -WebInspector.WebSocketConnection.Create = function(url, onConnectionReady) -{ - new WebInspector.WebSocketConnection(url, onConnectionReady); -} - -WebInspector.WebSocketConnection.prototype = { - - /** - * @param {!MessageEvent} message - */ - _onMessage: function(message) - { - var data = /** @type {string} */ (message.data); - this.dispatch(data); - }, - - /** - * @param {!Event} error - */ - _onError: function(error) - { - console.error(error); - }, - - /** - * @override - * @param {!Object} messageObject - */ - sendMessage: function(messageObject) - { - var message = JSON.stringify(messageObject); - this._socket.send(message); - }, - - __proto__: InspectorBackendClass.Connection.prototype -} - -/** - * @constructor - * @extends {InspectorBackendClass.Connection} - */ -WebInspector.StubConnection = function() -{ - InspectorBackendClass.Connection.call(this); -} - -WebInspector.StubConnection.prototype = { - /** - * @override - * @param {!Object} messageObject - */ - sendMessage: function(messageObject) - { - setTimeout(this._respondWithError.bind(this, messageObject), 0); - }, - - /** - * @param {!Object} messageObject - */ - _respondWithError: function(messageObject) - { - var error = { message: "This is a stub connection, can't dispatch message.", code: InspectorBackendClass.DevToolsStubErrorCode, data: messageObject }; - this.dispatch({ id: messageObject.id, error: error }); - }, - - __proto__: InspectorBackendClass.Connection.prototype -}
diff --git a/third_party/WebKit/Source/devtools/front_end/main/Main.js b/third_party/WebKit/Source/devtools/front_end/main/Main.js index 88677b8..b9439db8 100644 --- a/third_party/WebKit/Source/devtools/front_end/main/Main.js +++ b/third_party/WebKit/Source/devtools/front_end/main/Main.js
@@ -30,7 +30,6 @@ /** * @constructor - * @implements {InspectorAgent.Dispatcher} * @suppressGlobalPropertiesCheck */ WebInspector.Main = function() @@ -135,7 +134,7 @@ */ _createAppUI: function() { - console.timeStamp("Main._createApp"); + console.time("Main._createAppUI"); WebInspector.viewManager = new WebInspector.ViewManager(); @@ -210,6 +209,7 @@ this._registerMessageSinkListener(); self.runtime.extension(WebInspector.AppProvider).instance().then(this._showAppUI.bind(this)); + console.timeEnd("Main._createAppUI"); }, /** @@ -218,10 +218,10 @@ */ _showAppUI: function(appProvider) { + console.time("Main._showAppUI"); var app = /** @type {!WebInspector.AppProvider} */ (appProvider).createApp(); // It is important to kick controller lifetime after apps are instantiated. WebInspector.dockController.initialize(); - console.timeStamp("Main._presentUI"); app.presentUI(document); var toggleSearchNodeAction = WebInspector.actionRegistry.action("elements.toggle-element-search"); @@ -231,7 +231,6 @@ WebInspector.inspectorView.createToolbars(); InspectorFrontendHost.loadCompleted(); - InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.EvaluateForTestInFrontend, this._evaluateForTestInFrontend, this); InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.ReloadInspectedPage, this._reloadInspectedPage, this); var extensions = self.runtime.extensions(WebInspector.QueryParamHandler); @@ -253,43 +252,51 @@ this._appUIShown = true; if (this._fileSystemManagerInitialized) { // Allow UI cycles to repaint prior to creating connection. - setTimeout(this._createConnection.bind(this), 0); + setTimeout(this._connectAndCreateTarget.bind(this), 0); } + console.timeEnd("Main._showAppUI"); }, _didInitializeFileSystemManager: function() { this._fileSystemManagerInitialized = true; if (this._appUIShown) - this._createConnection(); + this._connectAndCreateTarget(); }, - _createConnection: function() + _connectAndCreateTarget: function() { - console.timeStamp("Main._createConnection"); + console.time("Main._connectAndCreateTarget"); + this._createConnection().then(connection => { + this._createMainTarget(connection); + InspectorFrontendHost.readyForTest(); + WebInspector.targetManager.setMainTargetFactory(this._recreateMainTarget.bind(this)); + // Asynchronously run the extensions. + console.timeEnd("Main._connectAndCreateTarget"); + setTimeout(this._lateInitialization.bind(this), 100); + }); + }, - if (Runtime.queryParam("ws")) { - var ws = "ws://" + Runtime.queryParam("ws"); - WebInspector.WebSocketConnection.Create(ws, this._connectionEstablished.bind(this)); - return; - } - - if (!InspectorFrontendHost.isHostedMode()) { - this._connectionEstablished(new WebInspector.MainConnection()); - return; - } - - this._connectionEstablished(new WebInspector.StubConnection()); + _recreateMainTarget: function() + { + this._createConnection().then(this._createMainTarget.bind(this)); }, /** - * @param {!InspectorBackendClass.Connection} connection + * @return {!Promise<!InspectorBackendClass.Connection>} */ - _connectionEstablished: function(connection) + _createConnection: function() { - console.timeStamp("Main._connectionEstablished"); - this._mainConnection = connection; - connection.addEventListener(InspectorBackendClass.Connection.Events.Disconnected, onDisconnected); + if (Runtime.queryParam("ws")) { + var ws = "ws://" + Runtime.queryParam("ws"); + return WebInspector.WebSocketConnection.Create(ws).then(connection => { + connection.addEventListener(InspectorBackendClass.Connection.Events.Disconnected, onDisconnected); + return connection; + }); + } + + return /** @type {!Promise<!InspectorBackendClass.Connection>} */ (Promise.resolve(InspectorFrontendHost.isHostedMode() ? + new WebInspector.StubConnection() : new WebInspector.MainConnection())); /** * @param {!WebInspector.Event} event @@ -300,32 +307,34 @@ return; WebInspector.RemoteDebuggingTerminatedScreen.show(event.data.reason); } - - this._createMainTarget(); - InspectorFrontendHost.readyForTest(); - // Asynchronously run the extensions. - setTimeout(this._lateInitialization.bind(this), 100); }, - _createMainTarget: function() + /** + * @param {!InspectorBackendClass.Connection} connection + */ + _createMainTarget: function(connection) { + console.time("Main._createMainTarget"); var capabilities = WebInspector.Target.Capability.Browser | WebInspector.Target.Capability.DOM | WebInspector.Target.Capability.JS | WebInspector.Target.Capability.Log | WebInspector.Target.Capability.Network | WebInspector.Target.Capability.Worker; - if (Runtime.queryParam("isSharedWorker")) + if (Runtime.queryParam("isSharedWorker")) { capabilities = WebInspector.Target.Capability.Browser | WebInspector.Target.Capability.Log | WebInspector.Target.Capability.Network | WebInspector.Target.Capability.Worker; - else if (Runtime.queryParam("v8only")) + } else if (Runtime.queryParam("v8only")) { capabilities = WebInspector.Target.Capability.JS; + } - this._mainTarget = WebInspector.targetManager.createTarget(WebInspector.UIString("Main"), capabilities, this._mainConnection, null); - this._mainTarget.registerInspectorDispatcher(this); - this._mainTarget.runtimeAgent().runIfWaitingForDebugger(); - if (this._mainTarget.hasBrowserCapability()) - this._mainTarget.inspectorAgent().enable(); - console.timeStamp("Main._mainTargetCreated"); + var target = WebInspector.targetManager.createTarget(WebInspector.UIString("Main"), capabilities, connection, null); + target.registerInspectorDispatcher(new WebInspector.Main.InspectorDomainDispatcher(target)); + target.runtimeAgent().runIfWaitingForDebugger(); + if (target.hasBrowserCapability()) + target.inspectorAgent().enable(); + if (Runtime.experiments.isEnabled("nodeDebugging")) + new WebInspector.RemoteLocationManager(target); + console.timeEnd("Main._createMainTarget"); }, _lateInitialization: function() @@ -333,8 +342,6 @@ console.timeStamp("Main._lateInitialization"); this._registerShortcuts(); WebInspector.extensionServer.initializeExtensions(); - if (Runtime.experiments.isEnabled("nodeDebugging")) - new WebInspector.RemoteLocationManager(this._mainTarget); }, _registerForwardedShortcuts: function() @@ -536,6 +543,24 @@ WebInspector.Main._reloadPage(hard); }, + _onSuspendStateChanged: function() + { + var suspended = WebInspector.targetManager.allTargetsSuspended(); + WebInspector.inspectorView.onSuspendStateChanged(suspended); + } +} + +/** + * @constructor + * @implements {InspectorAgent.Dispatcher} + * @param {!WebInspector.Target} target + */ +WebInspector.Main.InspectorDomainDispatcher = function(target) +{ + this._target = target; +} + +WebInspector.Main.InspectorDomainDispatcher.prototype = { /** * @override * @param {string} reason @@ -551,45 +576,13 @@ */ targetCrashed: function() { - var debuggerModel = WebInspector.DebuggerModel.fromTarget(this._mainTarget); + var debuggerModel = WebInspector.DebuggerModel.fromTarget(this._target); if (debuggerModel) WebInspector.TargetCrashedScreen.show(debuggerModel); - }, - - _onSuspendStateChanged: function() - { - var suspended = WebInspector.targetManager.allTargetsSuspended(); - WebInspector.inspectorView.onSuspendStateChanged(suspended); - }, - - /** - * @param {!WebInspector.Event} event - */ - _evaluateForTestInFrontend: function(event) - { - if (!InspectorFrontendHost.isUnderTest()) - return; - - var callId = /** @type {number} */ (event.data["callId"]); - var script = /** @type {number} */ (event.data["script"]); - - /** - * @suppressGlobalPropertiesCheck - */ - function invokeMethod() - { - try { - script = script + "//# sourceURL=evaluateInWebInspector" + callId + ".js"; - window.eval(script); - } catch (e) { - console.error(e.stack); - } - } - - this._mainConnection.deprecatedRunAfterPendingDispatches(invokeMethod); } } + /** * @constructor * @implements {WebInspector.ActionDelegate}
diff --git a/third_party/WebKit/Source/devtools/front_end/main/RemoteLocationManager.js b/third_party/WebKit/Source/devtools/front_end/main/RemoteLocationManager.js index 16c032a9..a613dc3 100644 --- a/third_party/WebKit/Source/devtools/front_end/main/RemoteLocationManager.js +++ b/third_party/WebKit/Source/devtools/front_end/main/RemoteLocationManager.js
@@ -45,7 +45,7 @@ * @param {?Protocol.Error} error * @return {!Promise} */ - _requestTargets: function (error) + _requestTargets: function(error) { if (error) { console.error(error); @@ -79,7 +79,7 @@ var target = this._connectedTargets.get(targetId); this._connectedTargets.delete(targetId); if (target) - WebInspector.targetManager.removeTarget(target); + target.dispose(); } } @@ -140,9 +140,12 @@ this._agent.sendMessage(this._targetId, JSON.stringify(messageObject)); }, - _close: function() + /** + * @override + */ + forceClose: function() { - this.connectionClosed("node_detached"); + this._agent.detach(this._targetId, () => {}); }, __proto__: InspectorBackendClass.Connection.prototype
diff --git a/third_party/WebKit/Source/devtools/front_end/main/module.json b/third_party/WebKit/Source/devtools/front_end/main/module.json index f1ccfc6..28f81872 100644 --- a/third_party/WebKit/Source/devtools/front_end/main/module.json +++ b/third_party/WebKit/Source/devtools/front_end/main/module.json
@@ -360,7 +360,6 @@ "RenderingOptions.js", "SimpleApp.js", "OverlayController.js", - "Connections.js", "Main.js", "RemoteLocationManager.js" ],
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/Connections.js b/third_party/WebKit/Source/devtools/front_end/sdk/Connections.js new file mode 100644 index 0000000..201c39d --- /dev/null +++ b/third_party/WebKit/Source/devtools/front_end/sdk/Connections.js
@@ -0,0 +1,255 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @constructor + * @extends {InspectorBackendClass.Connection} + */ +WebInspector.MainConnection = function() +{ + InspectorBackendClass.Connection.call(this); + InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.DispatchMessage, this._dispatchMessage, this); + InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.DispatchMessageChunk, this._dispatchMessageChunk, this); + InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.EvaluateForTestInFrontend, this._evaluateForTestInFrontend, this); +} + +WebInspector.MainConnection.prototype = { + /** + * @override + * @param {!Object} messageObject + */ + sendMessage: function(messageObject) + { + var message = JSON.stringify(messageObject); + InspectorFrontendHost.sendMessageToBackend(message); + }, + + /** + * @param {!WebInspector.Event} event + */ + _dispatchMessage: function(event) + { + this.dispatch(/** @type {string} */ (event.data)); + }, + + /** + * @param {!WebInspector.Event} event + */ + _dispatchMessageChunk: function(event) + { + var messageChunk = /** @type {string} */ (event.data["messageChunk"]); + var messageSize = /** @type {number} */ (event.data["messageSize"]); + if (messageSize) { + this._messageBuffer = ""; + this._messageSize = messageSize; + } + this._messageBuffer += messageChunk; + if (this._messageBuffer.length === this._messageSize) { + this.dispatch(this._messageBuffer); + this._messageBuffer = ""; + this._messageSize = 0; + } + }, + + /** + * @param {!WebInspector.Event} event + */ + _evaluateForTestInFrontend: function(event) + { + if (!InspectorFrontendHost.isUnderTest()) + return; + + var callId = /** @type {number} */ (event.data["callId"]); + var script = /** @type {number} */ (event.data["script"]); + + /** + * @suppressGlobalPropertiesCheck + */ + function invokeMethod() + { + try { + script = script + "//# sourceURL=evaluateInWebInspector" + callId + ".js"; + window.eval(script); + } catch (e) { + console.error(e.stack); + } + } + + this.deprecatedRunAfterPendingDispatches(invokeMethod); + }, + + /** + * @override + */ + forceClose: function() + { + InspectorFrontendHost.events.removeEventListener(InspectorFrontendHostAPI.Events.DispatchMessage, this._dispatchMessage, this); + InspectorFrontendHost.events.removeEventListener(InspectorFrontendHostAPI.Events.DispatchMessageChunk, this._dispatchMessageChunk, this); + InspectorFrontendHost.events.removeEventListener(InspectorFrontendHostAPI.Events.EvaluateForTestInFrontend, this._evaluateForTestInFrontend, this); + }, + + __proto__: InspectorBackendClass.Connection.prototype +} + +/** + * @constructor + * @extends {InspectorBackendClass.Connection} + * @param {string} url + * @param {function(!InspectorBackendClass.Connection)} onConnectionReady + */ +WebInspector.WebSocketConnection = function(url, onConnectionReady) +{ + InspectorBackendClass.Connection.call(this); + this._socket = new WebSocket(url); + this._socket.onmessage = this._onMessage.bind(this); + this._socket.onerror = this._onError.bind(this); + this._socket.onopen = onConnectionReady.bind(null, this); + this._socket.onclose = this.connectionClosed.bind(this, "websocket_closed"); +} + +/** + * @param {string} url + * @return {!Promise<!InspectorBackendClass.Connection>} + */ +WebInspector.WebSocketConnection.Create = function(url) +{ + var fulfill; + var result = new Promise(resolve => fulfill = resolve); + new WebInspector.WebSocketConnection(url, fulfill); + return result; +} + +WebInspector.WebSocketConnection.prototype = { + + /** + * @param {!MessageEvent} message + */ + _onMessage: function(message) + { + var data = /** @type {string} */ (message.data); + this.dispatch(data); + }, + + /** + * @param {!Event} error + */ + _onError: function(error) + { + console.error(error); + }, + + /** + * @override + */ + forceClose: function() + { + this._socket.close(); + }, + + /** + * @override + * @param {!Object} messageObject + */ + sendMessage: function(messageObject) + { + var message = JSON.stringify(messageObject); + this._socket.send(message); + }, + + __proto__: InspectorBackendClass.Connection.prototype +} + +/** + * @constructor + * @extends {InspectorBackendClass.Connection} + */ +WebInspector.StubConnection = function() +{ + InspectorBackendClass.Connection.call(this); +} + +WebInspector.StubConnection.prototype = { + /** + * @override + * @param {!Object} messageObject + */ + sendMessage: function(messageObject) + { + setTimeout(this._respondWithError.bind(this, messageObject), 0); + }, + + /** + * @param {!Object} messageObject + */ + _respondWithError: function(messageObject) + { + var error = { message: "This is a stub connection, can't dispatch message.", code: InspectorBackendClass.DevToolsStubErrorCode, data: messageObject }; + this.dispatch({ id: messageObject.id, error: error }); + }, + + __proto__: InspectorBackendClass.Connection.prototype +} + + +/** + * @constructor + * @param {function(string)} dispatchCallback + * @param {function()} yieldCallback + */ +WebInspector.RawProtocolConnection = function(dispatchCallback, yieldCallback) +{ + InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.DispatchMessage, this._dispatchMessage, this); + InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.DispatchMessageChunk, this._dispatchMessageChunk, this); + this._dispatchCallback = dispatchCallback; + this._yieldCallback = yieldCallback; + this._isClosed = false; +} + +WebInspector.RawProtocolConnection.prototype = { + /** + * @param {string} message + */ + send: function(message) + { + if (this._isClosed) + return; + InspectorFrontendHost.sendMessageToBackend(message); + }, + + /** + * @param {!WebInspector.Event} event + */ + _dispatchMessage: function(event) + { + this._dispatchCallback(/** @type {string} */ (event.data)); + }, + + /** + * @param {!WebInspector.Event} event + */ + _dispatchMessageChunk: function(event) + { + var messageChunk = /** @type {string} */ (event.data["messageChunk"]); + var messageSize = /** @type {number} */ (event.data["messageSize"]); + if (messageSize) { + this._messageBuffer = ""; + this._messageSize = messageSize; + } + this._messageBuffer += messageChunk; + if (this._messageBuffer.length === this._messageSize) { + this._dispatchCallback(this._messageBuffer); + this._messageBuffer = ""; + this._messageSize = 0; + } + }, + + yieldConnection: function() + { + InspectorFrontendHost.events.removeEventListener(InspectorFrontendHostAPI.Events.DispatchMessage, this._dispatchMessage, this); + InspectorFrontendHost.events.removeEventListener(InspectorFrontendHostAPI.Events.DispatchMessageChunk, this._dispatchMessageChunk, this); + this._isClosed = true; + delete this._dispatchCallback; + this._yieldCallback(); + } +}
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/InspectorBackend.js b/third_party/WebKit/Source/devtools/front_end/sdk/InspectorBackend.js index b81fb233..cd0d3c1 100644 --- a/third_party/WebKit/Source/devtools/front_end/sdk/InspectorBackend.js +++ b/third_party/WebKit/Source/devtools/front_end/sdk/InspectorBackend.js
@@ -420,6 +420,19 @@ console.log(message); }, + close: function() + { + this.forceClose(); + this.connectionClosed("force close"); + }, + + /** + * @protected + */ + forceClose: function() + { + }, + /** * @protected * @param {string} reason
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/SubTargetsManager.js b/third_party/WebKit/Source/devtools/front_end/sdk/SubTargetsManager.js index 69f79f98..103613e 100644 --- a/third_party/WebKit/Source/devtools/front_end/sdk/SubTargetsManager.js +++ b/third_party/WebKit/Source/devtools/front_end/sdk/SubTargetsManager.js
@@ -65,7 +65,7 @@ dispose: function() { for (var connection of this._connections.values()) - connection._close(); + connection.close(); this._connections.clear(); this._attachedTargets.clear(); }, @@ -194,7 +194,7 @@ { var connection = this._connections.get(targetId); if (connection) - connection._close(); + connection._reportClosed(); this._connections.delete(targetId); var target = this._attachedTargets.get(targetId); this._attachedTargets.delete(targetId); @@ -297,7 +297,15 @@ this._agent.sendMessageToTarget(this._targetId, JSON.stringify(messageObject)); }, - _close: function() + /** + * @override + */ + forceClose: function() + { + this._agent.detachFromTarget(this._targetId); + }, + + _reportClosed: function() { this.connectionClosed("target_terminated"); },
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/Target.js b/third_party/WebKit/Source/devtools/front_end/sdk/Target.js index f6b0db1..9db628e 100644 --- a/third_party/WebKit/Source/devtools/front_end/sdk/Target.js +++ b/third_party/WebKit/Source/devtools/front_end/sdk/Target.js
@@ -22,7 +22,7 @@ this._capabilitiesMask = capabilitiesMask; this._connection = connection; this._parentTarget = parentTarget; - connection.addEventListener(InspectorBackendClass.Connection.Events.Disconnected, this._onDisconnect, this); + connection.addEventListener(InspectorBackendClass.Connection.Events.Disconnected, this.dispose, this); this._id = WebInspector.Target._nextId++; /** @type {!Map.<!Function, !WebInspector.SDKModel>} */ @@ -78,7 +78,6 @@ }, /** - * * @return {!InspectorBackendClass.Connection} */ connection: function() @@ -161,15 +160,11 @@ return this._parentTarget; }, - _onDisconnect: function() + dispose: function() { this._targetManager.removeTarget(this); - this._dispose(); - }, - - _dispose: function() - { - this._targetManager.dispatchEventToListeners(WebInspector.TargetManager.Events.TargetDisposed, this); + for (var model of this._modelByConstructor.valuesArray()) + model.dispose(); if (this.workerManager) this.workerManager.dispose(); }, @@ -258,7 +253,6 @@ { WebInspector.SDKObject.call(this, target); target._modelByConstructor.set(modelClass, this); - WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.TargetDisposed, this._targetDisposed, this); } WebInspector.SDKModel.prototype = { @@ -289,7 +283,6 @@ if (target !== this._target) return; this.dispose(); - WebInspector.targetManager.removeEventListener(WebInspector.TargetManager.Events.TargetDisposed, this._targetDisposed, this); }, __proto__: WebInspector.SDKObject.prototype
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/TargetManager.js b/third_party/WebKit/Source/devtools/front_end/sdk/TargetManager.js index a98d55b..1ff7ec9 100644 --- a/third_party/WebKit/Source/devtools/front_end/sdk/TargetManager.js +++ b/third_party/WebKit/Source/devtools/front_end/sdk/TargetManager.js
@@ -236,6 +236,38 @@ }, /** + * @param {function()} factory + */ + setMainTargetFactory: function(factory) + { + this._mainTargetFactory = factory; + }, + + /** + * @param {function(string)} dispatch + * @return {!Promise<!WebInspector.RawProtocolConnection>} + */ + interceptMainConnection: function(dispatch) + { + var target = WebInspector.targetManager.mainTarget(); + if (target) + target.connection().close(); + + var fulfill; + var result = new Promise(resolve => fulfill = resolve); + InspectorFrontendHost.reattach(() => fulfill(new WebInspector.RawProtocolConnection(dispatch, yieldCallback.bind(this)))); + return result; + + /** + * @this {WebInspector.TargetManager} + */ + function yieldCallback() + { + InspectorFrontendHost.reattach(this._mainTargetFactory()); + } + }, + + /** * @param {!WebInspector.Target} target * @return {!Array<!WebInspector.TargetManager.Observer>} */ @@ -289,6 +321,8 @@ */ removeTarget: function(target) { + if (!this._targets.includes(target)) + return; this._targets.remove(target); var resourceTreeModel = WebInspector.ResourceTreeModel.fromTarget(target); var treeModelListeners = resourceTreeModel && resourceTreeModel[WebInspector.TargetManager._listenersSymbol]; @@ -309,14 +343,6 @@ } }, - removeAllTargets: function() - { - var targets = this._targets.slice(); - for (var i = targets.length - 1; i >=0 ; --i) - this.removeTarget(targets[i]); - this._targets = []; - }, - /** * @param {number=} capabilitiesMask * @return {!Array.<!WebInspector.Target>}
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/WorkerManager.js b/third_party/WebKit/Source/devtools/front_end/sdk/WorkerManager.js index 1dd507d..1969ff6a 100644 --- a/third_party/WebKit/Source/devtools/front_end/sdk/WorkerManager.js +++ b/third_party/WebKit/Source/devtools/front_end/sdk/WorkerManager.js
@@ -78,7 +78,7 @@ _reset: function() { for (var connection of this._connections.values()) - connection._close(); + connection.close(); this._connections.clear(); this._targetsByWorkerId.clear(); }, @@ -119,7 +119,7 @@ { var connection = this._connections.get(workerId); if (connection) - connection._close(); + connection._reportClosed(); this._connections.delete(workerId); this._targetsByWorkerId.delete(workerId); }, @@ -223,7 +223,7 @@ this._agent.sendMessageToWorker(this._workerId, JSON.stringify(messageObject)); }, - _close: function() + _reportClosed: function() { this.connectionClosed("worker_terminated"); },
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/module.json b/third_party/WebKit/Source/devtools/front_end/sdk/module.json index c7eb080..8eaba34 100644 --- a/third_party/WebKit/Source/devtools/front_end/sdk/module.json +++ b/third_party/WebKit/Source/devtools/front_end/sdk/module.json
@@ -77,6 +77,7 @@ "Target.js", "TargetManager.js", "ApplicationCacheModel.js", + "Connections.js", "ConsoleModel.js", "ContentProviders.js", "CookieParser.js",
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js b/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js index eb4a185..8ec159f 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js +++ b/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js
@@ -170,8 +170,8 @@ userEnteredText: function() { var text = this.text(); - if (this.autoCompleteElement) { - var addition = this.autoCompleteElement.textContent; + if (this._autocompleteElement) { + var addition = this._autocompleteElement.textContent; text = text.substring(0, text.length - addition.length); } return text; @@ -386,11 +386,11 @@ delete this._completeTimeout; } - if (!this.autoCompleteElement) + if (!this._autocompleteElement) return; - this.autoCompleteElement.remove(); - delete this.autoCompleteElement; + this._autocompleteElement.remove(); + delete this._autocompleteElement; delete this._userEnteredRange; delete this._userEnteredText; }, @@ -565,10 +565,10 @@ var prefixTextNode = createTextNode(prefixText); fullWordRange.insertNode(prefixTextNode); - this.autoCompleteElement = createElementWithClass("span", "auto-complete-text"); - this.autoCompleteElement.textContent = suffixText; + this._autocompleteElement = createElementWithClass("span", "auto-complete-text"); + this._autocompleteElement.textContent = suffixText; - prefixTextNode.parentNode.insertBefore(this.autoCompleteElement, prefixTextNode.nextSibling); + prefixTextNode.parentNode.insertBefore(this._autocompleteElement, prefixTextNode.nextSibling); finalSelectionRange.setStart(prefixTextNode, wordPrefixLength); finalSelectionRange.setEnd(prefixTextNode, wordPrefixLength); @@ -580,7 +580,7 @@ _completeCommonPrefix: function() { - if (!this.autoCompleteElement || !this._commonPrefix || !this._userEnteredText || !this._commonPrefix.startsWith(this._userEnteredText)) + if (!this._autocompleteElement || !this._commonPrefix || !this._userEnteredText || !this._commonPrefix.startsWith(this._userEnteredText)) return; if (!this.isSuggestBoxVisible()) { @@ -588,7 +588,7 @@ return; } - this.autoCompleteElement.textContent = this._commonPrefix.substring(this._userEnteredText.length); + this._autocompleteElement.textContent = this._commonPrefix.substring(this._userEnteredText.length); this._acceptSuggestionInternal(true); }, @@ -620,9 +620,9 @@ var finalSelectionRange = this._createRange(); var completionTextNode = createTextNode(completionText); this._userEnteredRange.insertNode(completionTextNode); - if (this.autoCompleteElement) { - this.autoCompleteElement.remove(); - delete this.autoCompleteElement; + if (this._autocompleteElement) { + this._autocompleteElement.remove(); + delete this._autocompleteElement; } if (isIntermediateSuggestion) @@ -653,13 +653,13 @@ */ _acceptSuggestionInternal: function(prefixAccepted) { - if (!this.autoCompleteElement || !this.autoCompleteElement.parentNode) + if (!this._autocompleteElement || !this._autocompleteElement.parentNode) return false; - var text = this.autoCompleteElement.textContent; + var text = this._autocompleteElement.textContent; var textNode = createTextNode(text); - this.autoCompleteElement.parentNode.replaceChild(textNode, this.autoCompleteElement); - delete this.autoCompleteElement; + this._autocompleteElement.parentNode.replaceChild(textNode, this._autocompleteElement); + delete this._autocompleteElement; var finalSelectionRange = this._createRange(); finalSelectionRange.setStart(textNode, text.length); @@ -719,7 +719,7 @@ var foundNextText = false; while (node) { if (node.nodeType === Node.TEXT_NODE && node.nodeValue.length) { - if (foundNextText && (!this.autoCompleteElement || !this.autoCompleteElement.isAncestor(node))) + if (foundNextText && (!this._autocompleteElement || !this._autocompleteElement.isAncestor(node))) return false; foundNextText = true; }
diff --git a/third_party/WebKit/Source/devtools/front_end/worker_service/ServiceDispatcher.js b/third_party/WebKit/Source/devtools/front_end/worker_service/ServiceDispatcher.js index fe4ff44..a9e36f3 100644 --- a/third_party/WebKit/Source/devtools/front_end/worker_service/ServiceDispatcher.js +++ b/third_party/WebKit/Source/devtools/front_end/worker_service/ServiceDispatcher.js
@@ -8,6 +8,9 @@ function Service() { } Service.prototype = { + /** + * @return {!Promise} + */ dispose: function() { } } @@ -46,7 +49,7 @@ return; } this._dispatchMessage(message); - } catch(e) { + } catch (e) { this._sendErrorResponse(message["id"], e.toString()); } },
diff --git a/third_party/WebKit/Source/devtools/package.json b/third_party/WebKit/Source/devtools/package.json index 1383f86..23600f0 100644 --- a/third_party/WebKit/Source/devtools/package.json +++ b/third_party/WebKit/Source/devtools/package.json
@@ -5,7 +5,9 @@ "start": "node scripts/start_chrome_and_server.js", "chrome": "node scripts/chrome_debug_launcher/launch_chrome.js", "server": "node scripts/hosted_mode/server.js", - "test": "echo \"Error: no test specified\" && exit 1", + "test": "node scripts/npm_test.js", + "test:build": "node scripts/npm_test.js --build-only", + "test:run": "node scripts/npm_test.js --test-only", "lint": "eslint -c front_end/.eslintrc.js --ignore-path front_end/.eslintignore front_end" }, "repository": {
diff --git a/third_party/WebKit/Source/devtools/scripts/npm_test.js b/third_party/WebKit/Source/devtools/scripts/npm_test.js new file mode 100644 index 0000000..dc14020 --- /dev/null +++ b/third_party/WebKit/Source/devtools/scripts/npm_test.js
@@ -0,0 +1,263 @@ +// 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. + +var childProcess = require("child_process"); +var fs = require("fs"); +var path = require("path"); +var shell = require("child_process").execSync; + +var utils = require("./utils"); + +var Flags = { + BUILD_ONLY: "--build-only", + DEBUG_DEVTOOLS: "--debug-devtools", + TEST_ONLY: "--test-only", +}; + +var IS_DEBUG_ENABLED = utils.includes(process.argv, Flags.DEBUG_DEVTOOLS); +var IS_BUILD_ONLY = utils.includes(process.argv, Flags.BUILD_ONLY); +var IS_TEST_ONLY = utils.includes(process.argv, Flags.TEST_ONLY); + +var CONTENT_SHELL_ZIP = "content-shell.zip"; +var MAX_CONTENT_SHELLS = 10; +var PLATFORM = getPlatform(); +var PYTHON = process.platform === "win32" ? "python.bat" : "python"; + +var BLINK_TEST_PATH = path.resolve(__dirname, "..", "..", "..", "..", "..", "blink", "tools", "run_layout_tests.py"); +var CACHE_PATH = path.resolve(__dirname, "..", ".test_cache"); +var SOURCE_PATH = path.resolve(__dirname, "..", "front_end"); + +function main(){ + if (IS_TEST_ONLY) { + findPreviousUploadedPosition(findMostRecentChromiumCommit()) + .then(commitPosition => runTests(path.resolve(CACHE_PATH, commitPosition, "out"))); + return; + } + if (!utils.isDir(CACHE_PATH)) + fs.mkdirSync(CACHE_PATH); + deleteOldContentShells(); + findPreviousUploadedPosition(findMostRecentChromiumCommit()) + .then(onUploadedCommitPosition) + .catch(onError); + + function onError(error) { + console.log("Unable to run tests because of error:", error); + console.log(`Try removing the .test_cache folder [${CACHE_PATH}] and retrying`); + } +} +main(); + +function onUploadedCommitPosition(commitPosition) +{ + var contentShellDirPath = path.resolve(CACHE_PATH, commitPosition, "out", "Release"); + var hasCachedContentShell = utils.isFile(getContentShellBinaryPath(contentShellDirPath)); + if (hasCachedContentShell) { + var contentShellPath = path.resolve(CACHE_PATH, commitPosition, "out"); + console.log(`Using cached content shell at: ${contentShellPath}`); + return buildAndTest(contentShellPath); + } + return prepareContentShellDirectory(commitPosition) + .then(downloadContentShell) + .then(extractContentShell) + .then(buildAndTest); +} + +function getPlatform() +{ + if (process.platform === "linux") { + if (process.arch === "x64") + return "Linux_x64"; + throw new Error("Pre-compiled content shells are only available for x64 on Linux"); + } + if (process.platform === "win32") { + if (process.arch === "x64") + return "Win_x64"; + return "Win"; + } + if (process.platform === "darwin") { + return "Mac"; + } + throw new Error(`Unrecognized platform detected: ${process.platform}`); +} + +function findMostRecentChromiumCommit() +{ + var commitMessage = shell(`git log --max-count=1 --grep="Cr-Commit-Position"`).toString().trim(); + var commitPosition = commitMessage.match(/Cr-Commit-Position: refs\/heads\/master@\{#([0-9]+)\}/)[1]; + return commitPosition; +} + +function deleteOldContentShells() +{ + var files = fs.readdirSync(CACHE_PATH); + if (files.length < MAX_CONTENT_SHELLS) + return; + files.sort((a, b) => parseInt(b, 10) - parseInt(a, 10)); + var remainingNumberOfContentShells = MAX_CONTENT_SHELLS / 2; + var oldContentShellDirs = files.slice(remainingNumberOfContentShells); + for (var i = 0; i < oldContentShellDirs.length; i++) + utils.removeRecursive(path.resolve(CACHE_PATH, oldContentShellDirs[i])); + console.log(`Removed old content shells: ${oldContentShellDirs}`) +} + +function findPreviousUploadedPosition(commitPosition) +{ + var previousPosition = commitPosition - 100; + var positionsListURL = `http://commondatastorage.googleapis.com/chromium-browser-snapshots/?delimiter=/&prefix=${PLATFORM}/&marker=${PLATFORM}/${previousPosition}/`; + return utils.fetch(positionsListURL) + .then(onPositionsList) + .catch(onError); + + function onPositionsList(buffer) + { + var positions = buffer.toString("binary") + .match(/([^<>]+)(?=<\/Prefix><\/CommonPrefixes>)/g) + .map(prefixedPosition => prefixedPosition.split("/")[1]) + .map(positionString => parseInt(positionString, 10)); + var positionSet = new Set(positions); + var previousUploadedPosition = commitPosition; + while (commitPosition - previousUploadedPosition < 100) { + if (positionSet.has(previousUploadedPosition)) + return previousUploadedPosition.toString(); + previousUploadedPosition--; + } + onError(); + } + + function onError(error) + { + if (error) + console.log(`Received error: ${error} trying to fetch positions list from url: ${positionsListURL}`); + throw new Error(`Unable to find a previous upload position for commit position: ${commitPosition}`); + } +} + +function prepareContentShellDirectory(contentShellCommitPosition) +{ + var contentShellPath = path.join(CACHE_PATH, contentShellCommitPosition); + if (utils.isDir(contentShellPath)) + utils.removeRecursive(contentShellPath); + fs.mkdirSync(contentShellPath); + return Promise.resolve(contentShellCommitPosition); +} + +function downloadContentShell(commitPosition) +{ + var url = `http://commondatastorage.googleapis.com/chromium-browser-snapshots/${PLATFORM}/${commitPosition}/${CONTENT_SHELL_ZIP}`; + console.log("Downloading content shell from:", url); + console.log("NOTE: Download is ~35-65 MB depending on OS"); + return utils.fetch(url) + .then(writeZip) + .catch(onError); + + function writeZip(buffer) + { + console.log("Completed download of content shell"); + var contentShellZipPath = path.join(CACHE_PATH, commitPosition, CONTENT_SHELL_ZIP); + fs.writeFileSync(contentShellZipPath, buffer); + return contentShellZipPath; + } + + function onError(error) + { + console.log(`Received error: ${error} trying to download content shell from url: ${url}`); + throw new Error("Unable to download content shell"); + } +} + +function extractContentShell(contentShellZipPath) +{ + console.log(`Extracting content shell zip: ${contentShellZipPath}`); + var unzipScriptPath = path.resolve(__dirname, "unzip.py"); + var src = contentShellZipPath; + var dest = path.resolve(path.dirname(src), "out"); + shell(`${PYTHON} ${unzipScriptPath} ${src} ${dest}`); + fs.unlinkSync(src); + var originalDirPath = path.resolve(dest, "content-shell"); + var newDirPath = path.resolve(dest, "Release"); + fs.renameSync(originalDirPath, newDirPath); + fs.chmodSync(getContentShellBinaryPath(newDirPath), "755"); + if (process.platform === "darwin") { + var helperPath = path.resolve(newDirPath, "Content Shell.app", "Contents", "Frameworks", "Content Shell Helper.app", "Contents", "MacOS", "Content Shell Helper"); + fs.chmodSync(helperPath, "755"); + } + return dest; +} + +function getContentShellBinaryPath(dirPath) +{ + if (process.platform === "linux") { + return path.resolve(dirPath, "content_shell"); + } + if (process.platform === "win32") { + return path.resolve(dirPath, "content_shell.exe"); + } + if (process.platform === "darwin") { + return path.resolve(dirPath, "Content Shell.app", "Contents", "MacOS", "Content Shell"); + } +} + +function buildAndTest(buildDirectoryPath) +{ + var contentShellResourcesPath = path.resolve(buildDirectoryPath, "Release", "resources"); + build(contentShellResourcesPath); + if (IS_BUILD_ONLY) + return; + runTests(buildDirectoryPath); +} + +function build(contentShellResourcesPath) +{ + var devtoolsResourcesPath = path.resolve(contentShellResourcesPath, "inspector"); + var copiedFrontendPath = path.resolve(devtoolsResourcesPath, "front_end"); + var debugPath = path.resolve(devtoolsResourcesPath, "debug"); + utils.removeRecursive(copiedFrontendPath); + utils.removeRecursive(debugPath); + utils.copyRecursive(SOURCE_PATH, devtoolsResourcesPath); + fs.renameSync(copiedFrontendPath, debugPath); + var inspectorBackendCommandsPath = path.resolve(devtoolsResourcesPath, "InspectorBackendCommands.js"); + var supportedCSSPropertiesPath = path.resolve(devtoolsResourcesPath, "SupportedCSSProperties.js"); + utils.copy(inspectorBackendCommandsPath, debugPath); + utils.copy(supportedCSSPropertiesPath, debugPath); +} + +function runTests(buildDirectoryPath) +{ + var testArgs = [ + "--additional-drt-flag=--debug-devtools", + "--no-pixel-tests", + "--build-directory", + buildDirectoryPath, + ].concat(getInspectorTests()); + if (IS_DEBUG_ENABLED) { + testArgs.push("--additional-drt-flag=--remote-debugging-port=9222"); + testArgs.push("--time-out-ms=6000000"); + console.log("\n============================================="); + console.log("Go to: http://localhost:9222/"); + console.log("Click on link and in console execute: test()"); + console.log("=============================================\n"); + } + var args = [BLINK_TEST_PATH].concat(testArgs).concat(getTestFlags()); + console.log(`Running layout tests with args: ${args}`); + childProcess.spawn(PYTHON, args, {stdio: "inherit"}); +} + +function getTestFlags() +{ + var flagValues = Object.keys(Flags).map(key => Flags[key]); + return process.argv + .slice(2) + .filter(arg => !utils.includes(flagValues, arg) && !utils.includes(arg, "inspector")); +} + +function getInspectorTests() +{ + var specificTests = process.argv.filter(arg => utils.includes(arg, "inspector")); + if (specificTests.length) + return specificTests; + return [ + "inspector*", + "http/tests/inspector*", + ]; +} \ No newline at end of file
diff --git a/third_party/WebKit/Source/devtools/scripts/unzip.py b/third_party/WebKit/Source/devtools/scripts/unzip.py new file mode 100755 index 0000000..68f5eae --- /dev/null +++ b/third_party/WebKit/Source/devtools/scripts/unzip.py
@@ -0,0 +1,20 @@ +#!/usr/bin/env python +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys +import zipfile + +if len(sys.argv) < 3: + print('Usage: {} <src> <dest>'.format(sys.argv[0])) + print(' <src> full path to zip file to be extracted') + print(' <dest> full path to destination folder') + sys.exit(1) + +src = sys.argv[1] +dest = sys.argv[2] + +zip_ref = zipfile.ZipFile(src, 'r') +zip_ref.extractall(dest) +zip_ref.close()
diff --git a/third_party/WebKit/Source/devtools/scripts/utils.js b/third_party/WebKit/Source/devtools/scripts/utils.js index c66e6a0..a0df35a 100644 --- a/third_party/WebKit/Source/devtools/scripts/utils.js +++ b/third_party/WebKit/Source/devtools/scripts/utils.js
@@ -5,6 +5,7 @@ var fs = require("fs"); var http = require("http"); var https = require("https"); +var path = require("path"); var parseURL = require("url").parse; var Stream = require("stream").Transform; @@ -45,18 +46,88 @@ return new Buffer(str, "base64").toString("binary"); } - function isFile(path) { try { return fs.statSync(path).isFile(); - } catch (e) { + } catch (error) { return false; } } +function isDir(path) +{ + try { + return fs.statSync(path).isDirectory(); + } catch (error) { + return false; + } +} + +function copy(src, dest) +{ + try { + var targetFilePath = path.resolve(dest, path.basename(src)); + fs.writeFileSync(targetFilePath, fs.readFileSync(src)); + } catch (error) { + throw new Error(`Received an error: [${error}] while trying to copy: ${src} -> ${dest}`); + } +} + +function copyRecursive(src, dest) +{ + try { + var targetDirPath = path.resolve(dest, path.basename(src)); + if (!fs.existsSync(targetDirPath)) + fs.mkdirSync(targetDirPath); + if (isDir(src)) { + var files = fs.readdirSync(src); + for (var i = 0; i < files.length; i++) { + var childPath = path.resolve(src, files[i]); + if (isDir(childPath)) { + copyRecursive(childPath, targetDirPath); + } else { + var targetFilePath = path.resolve(targetDirPath, path.basename(childPath)); + fs.writeFileSync(targetFilePath, fs.readFileSync(childPath)); + } + } + } + } catch (error) { + throw new Error(`Received an error: [${error}] while trying to copy: ${src} -> ${dest}`); + } +} + +function removeRecursive(filePath) +{ + try { + if (fs.existsSync(filePath)) { + var files = fs.readdirSync(filePath); + for (var i = 0; i < files.length; i++) { + var childPath = path.resolve(filePath, files[i]); + if (isDir(childPath)) + removeRecursive(childPath); + else + fs.unlinkSync(childPath); + } + fs.rmdirSync(filePath); + } + } catch (error) { + throw new Error(`Received an error: [${error}] while trying to remove: ${filePath}`); + } +} + +function includes(sequence, target) +{ + return sequence.indexOf(target) > -1; +} + module.exports = { fetch, atob, isFile, + isDir, + copy, + copyRecursive, + removeRecursive, + includes, }; \ No newline at end of file
diff --git a/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp index 2f8dca03..b16cd1d4e 100644 --- a/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp +++ b/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp
@@ -328,7 +328,6 @@ if (getLayoutObject() && getLayoutObject()->isTextControl()) return true; - m_layoutObject->document().updateStyleAndLayoutTree(); if (getNode() && hasEditableStyle(*getNode())) return true; @@ -347,7 +346,6 @@ // Requires layoutObject to be present because it relies on style // user-modify. Don't move this logic to AXNodeObject. bool AXLayoutObject::isRichlyEditable() const { - m_layoutObject->document().updateStyleAndLayoutTree(); if (getNode() && hasRichlyEditableStyle(*getNode())) return true;
diff --git a/third_party/WebKit/Source/modules/encryptedmedia/ContentDecryptionModuleResultPromise.cpp b/third_party/WebKit/Source/modules/encryptedmedia/ContentDecryptionModuleResultPromise.cpp index f11d53c..3bd4dd7 100644 --- a/third_party/WebKit/Source/modules/encryptedmedia/ContentDecryptionModuleResultPromise.cpp +++ b/third_party/WebKit/Source/modules/encryptedmedia/ContentDecryptionModuleResultPromise.cpp
@@ -6,6 +6,7 @@ #include "bindings/core/v8/ScriptPromise.h" #include "bindings/core/v8/ScriptState.h" +#include "bindings/core/v8/V8ThrowException.h" #include "core/dom/DOMException.h" #include "core/dom/ExecutionContext.h" #include "core/dom/ExecutionContextTask.h" @@ -17,22 +18,16 @@ ExceptionCode WebCdmExceptionToExceptionCode( WebContentDecryptionModuleException cdmException) { switch (cdmException) { + case WebContentDecryptionModuleExceptionTypeError: + return V8TypeError; case WebContentDecryptionModuleExceptionNotSupportedError: return NotSupportedError; case WebContentDecryptionModuleExceptionInvalidStateError: return InvalidStateError; - case WebContentDecryptionModuleExceptionInvalidAccessError: - return InvalidAccessError; case WebContentDecryptionModuleExceptionQuotaExceededError: return QuotaExceededError; case WebContentDecryptionModuleExceptionUnknownError: return UnknownError; - case WebContentDecryptionModuleExceptionClientError: - case WebContentDecryptionModuleExceptionOutputError: - // Currently no matching DOMException for these 2 errors. - // FIXME: Update DOMException to handle these if actually added to - // the EME spec. - return UnknownError; } NOTREACHED(); @@ -98,7 +93,10 @@ const String& errorMessage) { DCHECK(isValidToFulfillPromise()); - m_resolver->reject(DOMException::create(code, errorMessage)); + ScriptState::Scope scope(m_resolver->getScriptState()); + v8::Isolate* isolate = m_resolver->getScriptState()->isolate(); + m_resolver->reject( + V8ThrowException::createDOMException(isolate, code, errorMessage)); m_resolver.clear(); }
diff --git a/third_party/WebKit/Source/modules/encryptedmedia/HTMLMediaElementEncryptedMedia.cpp b/third_party/WebKit/Source/modules/encryptedmedia/HTMLMediaElementEncryptedMedia.cpp index 9db401a..9f7e9c7 100644 --- a/third_party/WebKit/Source/modules/encryptedmedia/HTMLMediaElementEncryptedMedia.cpp +++ b/third_party/WebKit/Source/modules/encryptedmedia/HTMLMediaElementEncryptedMedia.cpp
@@ -8,7 +8,7 @@ #include "bindings/core/v8/ScriptPromise.h" #include "bindings/core/v8/ScriptPromiseResolver.h" #include "bindings/core/v8/ScriptState.h" -#include "bindings/core/v8/V8Binding.h" +#include "bindings/core/v8/V8ThrowException.h" #include "core/dom/DOMException.h" #include "core/dom/DOMTypedArray.h" #include "core/dom/ExceptionCode.h" @@ -58,7 +58,7 @@ typedef Function<void(ExceptionCode, const String&)> FailureCallback; // Represents the result used when setContentDecryptionModule() is called. -// Calls |success| if result is resolved, |failure| is result is rejected. +// Calls |success| if result is resolved, |failure| if result is rejected. class SetContentDecryptionModuleResult final : public ContentDecryptionModuleResult { public: @@ -96,6 +96,7 @@ result.appendNumber(systemCode); result.append(')'); } + (*m_failureCallback)(WebCdmExceptionToExceptionCode(code), result.toString()); } @@ -250,7 +251,9 @@ !HTMLMediaElementEncryptedMedia::from(*m_element).m_isAttachingMediaKeys); // Reject promise with an appropriate error. - reject(DOMException::create(code, errorMessage)); + ScriptState::Scope scope(getScriptState()); + v8::Isolate* isolate = getScriptState()->isolate(); + reject(V8ThrowException::createDOMException(isolate, code, errorMessage)); } void SetMediaKeysHandler::clearFailed(ExceptionCode code,
diff --git a/third_party/WebKit/Source/modules/encryptedmedia/MediaKeySession.cpp b/third_party/WebKit/Source/modules/encryptedmedia/MediaKeySession.cpp index 4a21564..b248dba 100644 --- a/third_party/WebKit/Source/modules/encryptedmedia/MediaKeySession.cpp +++ b/third_party/WebKit/Source/modules/encryptedmedia/MediaKeySession.cpp
@@ -29,6 +29,7 @@ #include "bindings/core/v8/ScriptPromise.h" #include "bindings/core/v8/ScriptPromiseResolver.h" #include "bindings/core/v8/ScriptState.h" +#include "bindings/core/v8/V8ThrowException.h" #include "core/dom/DOMArrayBuffer.h" #include "core/dom/DOMException.h" #include "core/dom/ExceptionCode.h" @@ -81,6 +82,24 @@ return true; } +static bool IsPersistentSessionType(WebEncryptedMediaSessionType sessionType) { + // This implements section 5.1.1 Is persistent session type? from + // https://w3c.github.io/encrypted-media/#is-persistent-session-type + switch (sessionType) { + case WebEncryptedMediaSessionType::Temporary: + return false; + case WebEncryptedMediaSessionType::PersistentLicense: + return true; + case WebEncryptedMediaSessionType::PersistentReleaseMessage: + return true; + case blink::WebEncryptedMediaSessionType::Unknown: + break; + } + + NOTREACHED(); + return false; +} + static String ConvertKeyStatusToString( const WebEncryptedMediaKeyInformation::KeyStatus status) { switch (status) { @@ -111,6 +130,13 @@ DOMException::create(InvalidStateError, "The session is not callable.")); } +static ScriptPromise CreateRejectedPromiseAlreadyClosed( + ScriptState* scriptState) { + return ScriptPromise::rejectWithDOMException( + scriptState, DOMException::create(InvalidStateError, + "The session is already closed.")); +} + static ScriptPromise CreateRejectedPromiseAlreadyInitialized( ScriptState* scriptState) { return ScriptPromise::rejectWithDOMException( @@ -427,32 +453,38 @@ // Generates a request based on the initData. When this method is invoked, // the user agent must run the following steps: - // 1. If this object's uninitialized value is false, return a promise - // rejected with a new DOMException whose name is "InvalidStateError". + // 1. If this object is closed, return a promise rejected with an + // InvalidStateError. + if (m_isClosed) + return CreateRejectedPromiseAlreadyClosed(scriptState); + + // 2. If this object's uninitialized value is false, return a promise + // rejected with an InvalidStateError. if (!m_isUninitialized) return CreateRejectedPromiseAlreadyInitialized(scriptState); - // 2. Let this object's uninitialized be false. + // 3. Let this object's uninitialized be false. m_isUninitialized = false; - // 3. If initDataType is an empty string, return a promise rejected with a - // new DOMException whose name is "InvalidAccessError". + // 4. If initDataType is the empty string, return a promise rejected + // with a newly created TypeError. if (initDataTypeString.isEmpty()) { - return ScriptPromise::rejectWithDOMException( + return ScriptPromise::reject( scriptState, - DOMException::create(InvalidAccessError, - "The initDataType parameter is empty.")); + V8ThrowException::createTypeError( + scriptState->isolate(), "The initDataType parameter is empty.")); } - // 4. If initData is an empty array, return a promise rejected with a new - // DOMException whose name is"InvalidAccessError". + // 5. If initData is an empty array, return a promise rejected with a + // newly created TypeError. if (!initData.byteLength()) { - return ScriptPromise::rejectWithDOMException( - scriptState, DOMException::create(InvalidAccessError, + return ScriptPromise::reject( + scriptState, + V8ThrowException::createTypeError(scriptState->isolate(), "The initData parameter is empty.")); } - // 5. If the Key System implementation represented by this object's cdm + // 6. If the Key System implementation represented by this object's cdm // implementation value does not support initDataType as an // Initialization Data Type, return a promise rejected with a new // DOMException whose name is NotSupportedError. String comparison @@ -470,26 +502,26 @@ initDataTypeString + "' is not supported.")); } - // 6. Let init data be a copy of the contents of the initData parameter. + // 7. Let init data be a copy of the contents of the initData parameter. DOMArrayBuffer* initDataBuffer = DOMArrayBuffer::create(initData.data(), initData.byteLength()); - // 7. Let session type be this object's session type. + // 8. Let session type be this object's session type. // (Done in constructor.) - // 8. Let promise be a new promise. + // 9. Let promise be a new promise. NewSessionResultPromise* result = new NewSessionResultPromise(scriptState, this); ScriptPromise promise = result->promise(); - // 9. Run the following steps asynchronously (documented in - // actionTimerFired()) + // 10. Run the following steps asynchronously (documented in + // actionTimerFired()) m_pendingActions.append(PendingAction::CreatePendingGenerateRequest( result, initDataType, initDataBuffer)); DCHECK(!m_actionTimer.isActive()); m_actionTimer.startOneShot(0, BLINK_FROM_HERE); - // 10. Return promise. + // 11. Return promise. return promise; } @@ -502,39 +534,38 @@ // Loads the data stored for the specified session into this object. When // this method is invoked, the user agent must run the following steps: - // 1. If this object's uninitialized value is false, return a promise - // rejected with a new DOMException whose name is "InvalidStateError". + // 1. If this object is closed, return a promise rejected with an + // InvalidStateError. + if (m_isClosed) + return CreateRejectedPromiseAlreadyClosed(scriptState); + + // 2. If this object's uninitialized value is false, return a promise + // rejected with an InvalidStateError. if (!m_isUninitialized) return CreateRejectedPromiseAlreadyInitialized(scriptState); - // 2. Let this object's uninitialized be false. + // 3. Let this object's uninitialized value be false. m_isUninitialized = false; - // 3. If sessionId is an empty string, return a promise rejected with a - // new DOMException whose name is "InvalidAccessError". + // 4. If sessionId is the empty string, return a promise rejected with + // a newly created TypeError. if (sessionId.isEmpty()) { - return ScriptPromise::rejectWithDOMException( - scriptState, DOMException::create(InvalidAccessError, + return ScriptPromise::reject( + scriptState, + V8ThrowException::createTypeError(scriptState->isolate(), "The sessionId parameter is empty.")); } - // 4. If this object's session type is not "persistent-license" or - // "persistent-release-message", return a promise rejected with a - // new DOMException whose name is InvalidAccessError. - if (m_sessionType != WebEncryptedMediaSessionType::PersistentLicense && - m_sessionType != WebEncryptedMediaSessionType::PersistentReleaseMessage) { - return ScriptPromise::rejectWithDOMException( + // 5. If the result of running the "Is persistent session type?" algorithm + // on this object's session type is false, return a promise rejected + // with a newly created TypeError. + if (!IsPersistentSessionType(m_sessionType)) { + return ScriptPromise::reject( scriptState, - DOMException::create(InvalidAccessError, - "The session type is not persistent.")); + V8ThrowException::createTypeError( + scriptState->isolate(), "The session type is not persistent.")); } - // 5. If the Key System implementation represented by this object's cdm - // implementation value does not support loading previous sessions, - // return a promise rejected with a new DOMException whose name is - // NotSupportedError. - // FIXME: Implement this (http://crbug.com/448922). - // 6. Let origin be the origin of this object's Document. // (Available as getExecutionContext()->getSecurityOrigin() anytime.) @@ -557,41 +588,46 @@ ScriptPromise MediaKeySession::update(ScriptState* scriptState, const DOMArrayPiece& response) { DVLOG(MEDIA_KEY_SESSION_LOG_LEVEL) << __func__ << "(" << this << ")"; - DCHECK(!m_isClosed); // From https://w3c.github.io/encrypted-media/#update: // Provides messages, including licenses, to the CDM. When this method is // invoked, the user agent must run the following steps: - // 1. If this object's callable value is false, return a promise rejected - // with a new DOMException whose name is InvalidStateError. + // 1. If this object is closed, return a promise rejected with an + // InvalidStateError. + if (m_isClosed) + return CreateRejectedPromiseAlreadyClosed(scriptState); + + // 2. If this object's callable value is false, return a promise + // rejected with an InvalidStateError. if (!m_isCallable) return CreateRejectedPromiseNotCallable(scriptState); - // 2. If response is an empty array, return a promise rejected with a - // new DOMException whose name is InvalidAccessError. + // 3. If response is an empty array, return a promise rejected with a + // newly created TypeError. if (!response.byteLength()) { - return ScriptPromise::rejectWithDOMException( - scriptState, DOMException::create(InvalidAccessError, + return ScriptPromise::reject( + scriptState, + V8ThrowException::createTypeError(scriptState->isolate(), "The response parameter is empty.")); } - // 3. Let response copy be a copy of the contents of the response parameter. + // 4. Let response copy be a copy of the contents of the response parameter. DOMArrayBuffer* responseCopy = DOMArrayBuffer::create(response.data(), response.byteLength()); - // 4. Let promise be a new promise. + // 5. Let promise be a new promise. SimpleResultPromise* result = new SimpleResultPromise(scriptState, this); ScriptPromise promise = result->promise(); - // 5. Run the following steps asynchronously (documented in + // 6. Run the following steps asynchronously (documented in // actionTimerFired()) m_pendingActions.append( PendingAction::CreatePendingUpdate(result, responseCopy)); if (!m_actionTimer.isActive()) m_actionTimer.startOneShot(0, BLINK_FROM_HERE); - // 6. Return promise. + // 7. Return promise. return promise; } @@ -634,29 +670,24 @@ // Removes stored session data associated with this object. When this // method is invoked, the user agent must run the following steps: - // 1. If this object's callable value is false, return a promise rejected - // with a new DOMException whose name is "InvalidStateError". + // 1. If this object is closed, return a promise rejected with an + // InvalidStateError. + if (m_isClosed) + return CreateRejectedPromiseAlreadyClosed(scriptState); + + // 2. If this object's callable value is false, return a promise rejected + // with an InvalidStateError. if (!m_isCallable) return CreateRejectedPromiseNotCallable(scriptState); - // 2. If this object's session type is not "persistent-license" or - // "persistent-release-message", return a promise rejected with a - // new DOMException whose name is InvalidAccessError. - if (m_sessionType != WebEncryptedMediaSessionType::PersistentLicense && - m_sessionType != WebEncryptedMediaSessionType::PersistentReleaseMessage) { - return ScriptPromise::rejectWithDOMException( + // 3. If the result of running the "Is persistent session type?" algorithm + // on this object's session type is false, return a promise rejected + // with a newly created TypeError. + if (!IsPersistentSessionType(m_sessionType)) { + return ScriptPromise::reject( scriptState, - DOMException::create(InvalidAccessError, - "The session type is not persistent.")); - } - - // 3. If the Session Close algorithm has been run on this object, return a - // promise rejected with a new DOMException whose name is - // "InvalidStateError". - if (m_isClosed) { - return ScriptPromise::rejectWithDOMException( - scriptState, DOMException::create(InvalidStateError, - "The session is already closed.")); + V8ThrowException::createTypeError( + scriptState->isolate(), "The session type is not persistent.")); } // 4. Let promise be a new promise. @@ -687,18 +718,18 @@ switch (action->getType()) { case PendingAction::GenerateRequest: - // NOTE: Continue step 9 of MediaKeySession::generateRequest(). + // NOTE: Continue step 10 of MediaKeySession::generateRequest(). DVLOG(MEDIA_KEY_SESSION_LOG_LEVEL) << __func__ << "(" << this << ") GenerateRequest"; - // initializeNewSession() in Chromium will execute steps 9.1 to 9.7. + // initializeNewSession() in Chromium will execute steps 10.1 to 10.9. m_session->initializeNewSession( action->initDataType(), static_cast<unsigned char*>(action->data()->data()), action->data()->byteLength(), m_sessionType, action->result()->result()); - // Remaining steps (from 9.8) executed in finishGenerateRequest(), + // Remaining steps (from 10.10) executed in finishGenerateRequest(), // called when |result| is resolved. break; @@ -712,11 +743,11 @@ // validate the sessionId value before passing it to the CDM. // At a minimum, this should include checking that the length // and value (e.g. alphanumeric) are reasonable. - // 8.2 If the previous step failed, reject promise with a new - // DOMException whose name is "InvalidAccessError". + // 8.2 If the preceding step failed, or if sanitized session ID + // is empty, reject promise with a newly created TypeError. if (!isValidSessionId(action->sessionId())) { action->result()->completeWithError( - WebContentDecryptionModuleExceptionInvalidAccessError, 0, + WebContentDecryptionModuleExceptionTypeError, 0, "Invalid sessionId"); return; }
diff --git a/third_party/WebKit/Source/modules/encryptedmedia/MediaKeys.cpp b/third_party/WebKit/Source/modules/encryptedmedia/MediaKeys.cpp index ff36062..903174d 100644 --- a/third_party/WebKit/Source/modules/encryptedmedia/MediaKeys.cpp +++ b/third_party/WebKit/Source/modules/encryptedmedia/MediaKeys.cpp
@@ -25,7 +25,9 @@ #include "modules/encryptedmedia/MediaKeys.h" +#include "bindings/core/v8/ScriptPromise.h" #include "bindings/core/v8/ScriptState.h" +#include "bindings/core/v8/V8ThrowException.h" #include "core/dom/DOMArrayBuffer.h" #include "core/dom/DOMException.h" #include "core/dom/ExceptionCode.h" @@ -190,20 +192,21 @@ // The setServerCertificate(serverCertificate) method provides a server // certificate to be used to encrypt messages to the license server. // It must run the following steps: - // 1. If serverCertificate is an empty array, return a promise rejected - // with a new DOMException whose name is "InvalidAccessError". + // 1. If the Key System implementation represented by this object's cdm + // implementation value does not support server certificates, return + // a promise resolved with false. + // TODO(jrummell): Provide a way to determine if the CDM supports this. + // http://crbug.com/647816. + // + // 2. If serverCertificate is an empty array, return a promise rejected + // with a new a newly created TypeError. if (!serverCertificate.byteLength()) { - return ScriptPromise::rejectWithDOMException( - scriptState, - DOMException::create(InvalidAccessError, - "The serverCertificate parameter is empty.")); + return ScriptPromise::reject( + scriptState, V8ThrowException::createTypeError( + scriptState->isolate(), + "The serverCertificate parameter is empty.")); } - // 2. If the keySystem does not support server certificates, return a - // promise rejected with a new DOMException whose name is - // "NotSupportedError". - // (Let the CDM decide whether to support this or not.) - // 3. Let certificate be a copy of the contents of the serverCertificate // parameter. DOMArrayBuffer* serverCertificateBuffer = DOMArrayBuffer::create(
diff --git a/third_party/WebKit/Source/modules/encryptedmedia/NavigatorRequestMediaKeySystemAccess.cpp b/third_party/WebKit/Source/modules/encryptedmedia/NavigatorRequestMediaKeySystemAccess.cpp index 510e1dc..a81ab3a 100644 --- a/third_party/WebKit/Source/modules/encryptedmedia/NavigatorRequestMediaKeySystemAccess.cpp +++ b/third_party/WebKit/Source/modules/encryptedmedia/NavigatorRequestMediaKeySystemAccess.cpp
@@ -4,8 +4,10 @@ #include "modules/encryptedmedia/NavigatorRequestMediaKeySystemAccess.h" +#include "bindings/core/v8/ScriptPromise.h" #include "bindings/core/v8/ScriptPromiseResolver.h" #include "bindings/core/v8/ScriptState.h" +#include "bindings/core/v8/V8ThrowException.h" #include "core/dom/DOMException.h" #include "core/dom/Document.h" #include "core/dom/ExceptionCode.h" @@ -102,9 +104,7 @@ const override { return m_supportedConfigurations; } - SecurityOrigin* getSecurityOrigin() const override { - return m_resolver->getExecutionContext()->getSecurityOrigin(); - } + SecurityOrigin* getSecurityOrigin() const override; void requestSucceeded(WebContentDecryptionModuleAccess*) override; void requestNotSupported(const WebString& errorMessage) override; @@ -116,6 +116,9 @@ } private: + // Returns true if the ExecutionContext is valid, false otherwise. + bool isExecutionContextValid() const; + // For widevine key system, generate warning and report to UMA if // |m_supportedConfigurations| contains any video capability with empty // robustness string. @@ -192,11 +195,20 @@ checkVideoCapabilityRobustness(); } +SecurityOrigin* MediaKeySystemAccessInitializer::getSecurityOrigin() const { + return isExecutionContextValid() + ? m_resolver->getExecutionContext()->getSecurityOrigin() + : nullptr; +} + void MediaKeySystemAccessInitializer::requestSucceeded( WebContentDecryptionModuleAccess* access) { checkEmptyCodecs(access->getConfiguration()); checkCapabilities(access->getConfiguration()); + if (!isExecutionContextValid()) + return; + m_resolver->resolve( new MediaKeySystemAccess(m_keySystem, wrapUnique(access))); m_resolver.clear(); @@ -204,10 +216,21 @@ void MediaKeySystemAccessInitializer::requestNotSupported( const WebString& errorMessage) { + if (!isExecutionContextValid()) + return; + m_resolver->reject(DOMException::create(NotSupportedError, errorMessage)); m_resolver.clear(); } +bool MediaKeySystemAccessInitializer::isExecutionContextValid() const { + // activeDOMObjectsAreStopped() is called to see if the context is in the + // process of being destroyed. If it is true, assume the context is no + // longer valid as it is about to be destroyed anyway. + ExecutionContext* context = m_resolver->getExecutionContext(); + return context && !context->activeDOMObjectsAreStopped(); +} + void MediaKeySystemAccessInitializer::checkVideoCapabilityRobustness() const { // Only check for widevine key system. if (keySystem() != "com.widevine.alpha") @@ -312,21 +335,21 @@ // From https://w3c.github.io/encrypted-media/#requestMediaKeySystemAccess // When this method is invoked, the user agent must run the following steps: - // 1. If keySystem is an empty string, return a promise rejected with a - // new DOMException whose name is InvalidAccessError. + // 1. If keySystem is the empty string, return a promise rejected with a + // newly created TypeError. if (keySystem.isEmpty()) { - return ScriptPromise::rejectWithDOMException( - scriptState, DOMException::create(InvalidAccessError, + return ScriptPromise::reject( + scriptState, + V8ThrowException::createTypeError(scriptState->isolate(), "The keySystem parameter is empty.")); } - // 2. If supportedConfigurations was provided and is empty, return a - // promise rejected with a new DOMException whose name is - // InvalidAccessError. + // 2. If supportedConfigurations is empty, return a promise rejected with + // a newly created TypeError. if (!supportedConfigurations.size()) { - return ScriptPromise::rejectWithDOMException( - scriptState, DOMException::create( - InvalidAccessError, + return ScriptPromise::reject( + scriptState, V8ThrowException::createTypeError( + scriptState->isolate(), "The supportedConfigurations parameter is empty.")); }
diff --git a/third_party/WebKit/Source/modules/notifications/Notification.cpp b/third_party/WebKit/Source/modules/notifications/Notification.cpp index eea467b..af9ad5b 100644 --- a/third_party/WebKit/Source/modules/notifications/Notification.cpp +++ b/third_party/WebKit/Source/modules/notifications/Notification.cpp
@@ -137,8 +137,7 @@ ActiveDOMObject(context), m_type(type), m_state(State::Loading), - m_data(data), - m_requestedClose(false) { + m_data(data) { DCHECK(notificationManager()); } @@ -157,7 +156,7 @@ DCHECK_EQ(m_state, State::Loading); if (NotificationManager::from(getExecutionContext())->permissionStatus() != mojom::blink::PermissionStatus::GRANTED) { - dispatchEvent(Event::create(EventTypeNames::error)); + dispatchErrorEvent(); return; } @@ -180,55 +179,48 @@ } void Notification::close() { - if (m_state != State::Showing) { - // TODO(peter): Abort the load instead of closing the notification after - // it's completed. - if (m_state == State::Loading || m_notificationId.isEmpty()) - m_requestedClose = true; - + if (m_state != State::Showing) return; - } // Schedule the "close" event to be fired for non-persistent notifications. // Persistent notifications won't get such events for programmatic closes. if (m_type == Type::NonPersistent) { getExecutionContext()->postTask( - BLINK_FROM_HERE, - createSameThreadTask(&Notification::didCloseNotification, - wrapPersistent(this))); + BLINK_FROM_HERE, createSameThreadTask(&Notification::dispatchCloseEvent, + wrapPersistent(this))); m_state = State::Closing; - } else { - m_state = State::Closed; + + notificationManager()->close(this); + return; } + m_state = State::Closed; + SecurityOrigin* origin = getExecutionContext()->getSecurityOrigin(); DCHECK(origin); - notificationManager()->close(WebSecurityOrigin(origin), m_data.tag, - m_notificationId); + notificationManager()->closePersistent(WebSecurityOrigin(origin), m_data.tag, + m_notificationId); } -void Notification::didShowNotification(const WebString& notificationId) { - DCHECK(m_notificationId.isEmpty()); - m_notificationId = notificationId; - +void Notification::dispatchShowEvent() { dispatchEvent(Event::create(EventTypeNames::show)); - - if (m_requestedClose) - close(); } -void Notification::didClickNotification() { +void Notification::dispatchClickEvent() { UserGestureIndicator gestureIndicator( UserGestureToken::create(UserGestureToken::NewGesture)); - ScopedWindowFocusAllowedIndicator windowFocusAllowed(getExecutionContext()); dispatchEvent(Event::create(EventTypeNames::click)); } -void Notification::didCloseNotification() { - // The notification will be showing when the user initiated the close, or it - // will be closing if the developer initiated the close. +void Notification::dispatchErrorEvent() { + dispatchEvent(Event::create(EventTypeNames::error)); +} + +void Notification::dispatchCloseEvent() { + // The notification should be Showing if the user initiated the close, or it + // should be Closing if the developer initiated the close. if (m_state != State::Showing && m_state != State::Closing) return;
diff --git a/third_party/WebKit/Source/modules/notifications/Notification.h b/third_party/WebKit/Source/modules/notifications/Notification.h index 0d1b8eb..ef5b78d 100644 --- a/third_party/WebKit/Source/modules/notifications/Notification.h +++ b/third_party/WebKit/Source/modules/notifications/Notification.h
@@ -90,9 +90,10 @@ DEFINE_ATTRIBUTE_EVENT_LISTENER(close); // WebNotificationDelegate interface. - void didShowNotification(const WebString& notificationId) override; - void didClickNotification() override; - void didCloseNotification() override; + void dispatchShowEvent() override; + void dispatchClickEvent() override; + void dispatchErrorEvent() override; + void dispatchCloseEvent() override; String title() const; String dir() const; @@ -174,10 +175,6 @@ String m_notificationId; - // Whether the developer has requested the notification to be closed whilst - // it's still in process of being shown. - bool m_requestedClose; - Member<AsyncMethodRunner<Notification>> m_prepareShowMethodRunner; Member<NotificationResourcesLoader> m_loader;
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in index 432aa28..3f32fdb 100644 --- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in +++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
@@ -187,7 +187,7 @@ ReloadwithoutSubResourceCacheRevalidation RemotePlayback status=experimental RenderingPipelineThrottling status=stable -RenderUnicodeControlCharacters status=experimental +RenderUnicodeControlCharacters status=stable ResizeObserver status=experimental // Handles frame scrolling via the root PaintLayer instead of the FrameView. // crbug.com/417782 tracks enabling this by default.
diff --git a/third_party/WebKit/Source/web/WebSettingsImpl.cpp b/third_party/WebKit/Source/web/WebSettingsImpl.cpp index 736c51a..8fa5aac 100644 --- a/third_party/WebKit/Source/web/WebSettingsImpl.cpp +++ b/third_party/WebKit/Source/web/WebSettingsImpl.cpp
@@ -665,10 +665,6 @@ m_settings->setSmartInsertDeleteEnabled(enabled); } -void WebSettingsImpl::setPinchOverlayScrollbarThickness(int thickness) { - m_settings->setPinchOverlayScrollbarThickness(thickness); -} - void WebSettingsImpl::setUseSolidColorScrollbars(bool enabled) { m_settings->setUseSolidColorScrollbars(enabled); }
diff --git a/third_party/WebKit/Source/web/WebSettingsImpl.h b/third_party/WebKit/Source/web/WebSettingsImpl.h index c83fc04..e3238e7 100644 --- a/third_party/WebKit/Source/web/WebSettingsImpl.h +++ b/third_party/WebKit/Source/web/WebSettingsImpl.h
@@ -135,7 +135,6 @@ void setPerTilePaintingEnabled(bool) override; void setPictographFontFamily(const WebString&, UScriptCode = USCRIPT_COMMON) override; - void setPinchOverlayScrollbarThickness(int) override; void setPluginsEnabled(bool) override; void setAvailablePointerTypes(int) override; void setPrimaryPointerType(PointerType) override;
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/wptserve.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/wptserve.py index aaec9083..e9219a8 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/wptserve.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/wptserve.py
@@ -42,10 +42,7 @@ if self._port_obj.host.filesystem.exists(path_to_ws_handlers): start_cmd += ['--ws_doc_root', path_to_ws_handlers] - # TODO(tkent): Do not suppress console output on Windows until - # crbug.com/623613 is resolved. - if not self._platform.is_win(): - self._stdout = self._stderr = self._executive.DEVNULL + self._stdout = self._stderr = self._executive.DEVNULL # TODO(burnik): We should stop setting the CWD once WPT can be run without it. self._cwd = path_to_wpt_root self._env = port_obj.host.environ.copy()
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/01.pem b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/01.pem new file mode 100644 index 0000000..ff31326 --- /dev/null +++ b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/01.pem
@@ -0,0 +1,83 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=web-platform-tests + Validity + Not Before: Oct 18 08:36:53 2016 GMT + Not After : Jan 4 08:36:53 2025 GMT + Subject: CN=web-platform-tests + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c9:e3:69:85:18:ad:1a:df:ba:6c:05:96:0c:c1: + 22:51:78:3a:90:42:88:72:f7:f4:8c:81:59:43:5b: + ea:13:0e:e7:ac:27:c2:72:55:c6:53:48:4f:08:37: + a1:5f:e1:5c:78:24:d3:12:2d:2b:71:c3:51:a3:ad: + 0b:c6:a1:95:2d:f5:cf:db:50:64:c8:fa:d0:2a:bc: + ae:a8:2b:44:53:a2:a9:e8:55:92:b7:95:4f:d2:f3: + d9:f1:e1:0b:17:11:ad:62:50:c5:57:4d:43:ed:fc: + 6f:14:99:52:ab:27:f4:59:45:f8:86:77:d1:fc:00: + 8a:b3:4c:7e:66:1a:b3:04:58:e5:9e:57:52:b8:9d: + ef:cc:ef:59:6e:a0:99:2c:e1:e1:1b:da:19:be:61: + f9:c3:89:76:1e:88:1e:86:64:1f:5b:27:3e:44:7c: + ad:d0:04:b0:57:db:79:bd:1e:7d:4a:73:52:c9:2d: + 16:6d:f0:80:2e:07:70:09:2f:ca:a2:c3:12:ae:2c: + 51:3d:d4:f3:4e:f6:53:c2:9d:e0:6c:56:6d:36:38: + bc:1e:4f:d9:7e:1e:b9:26:fe:2f:f0:d6:6b:af:02: + bf:4d:93:4b:a7:e7:bd:eb:e7:79:69:b1:65:1f:88: + f0:fd:cc:03:ce:5e:00:83:b9:ad:70:6a:95:78:5b: + 97:fb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:TRUE + X509v3 Subject Key Identifier: + 4E:65:0D:3B:92:B3:5A:4B:34:75:FB:29:26:CA:53:6B:A1:65:15:7E + X509v3 Authority Key Identifier: + keyid:4E:65:0D:3B:92:B3:5A:4B:34:75:FB:29:26:CA:53:6B:A1:65:15:7E + DirName:/CN=web-platform-tests + serial:01 + + X509v3 Key Usage: + Certificate Sign + X509v3 Extended Key Usage: + TLS Web Server Authentication + Signature Algorithm: sha256WithRSAEncryption + c6:f0:d9:1e:24:82:3a:b8:d4:b3:92:00:ea:0e:d6:17:e9:8d: + a0:7a:35:bc:59:b9:2d:b0:6b:c2:27:74:66:6f:26:18:c2:75: + 14:4c:29:76:3d:28:3c:6e:66:be:3c:27:55:17:09:f3:f4:2d: + 1b:ab:b5:b5:5c:f6:5c:24:d1:7d:35:45:30:7e:41:19:79:f8: + a1:38:ca:ce:30:35:e0:de:1c:77:f8:fe:cd:dd:25:4c:13:18: + d7:9e:8b:35:04:58:24:7e:a3:f5:89:48:fc:28:e7:11:88:ec: + b8:37:e5:2f:8b:61:b9:f3:44:12:6a:62:cb:b3:16:5f:61:77: + 8c:e6:d5:3f:ec:43:9a:22:7b:d2:e0:3a:25:15:49:e4:75:09: + a3:9e:82:64:a9:3f:94:86:c7:a8:87:a2:34:51:87:c9:58:89: + 76:42:00:bc:7c:d3:17:7a:40:79:b1:f5:ac:0d:9d:14:a5:db: + 57:7f:10:36:21:79:7e:8f:62:91:68:d2:1f:c9:d3:76:18:cf: + 98:13:bf:21:a1:09:c3:40:17:9e:16:80:f4:61:23:46:cf:78: + 9b:00:a7:fa:53:00:b5:67:d3:82:84:79:50:c0:ae:e0:0f:51: + 55:84:e8:5a:cd:c7:6a:83:11:de:82:58:70:11:b8:1e:67:ab: + 30:1d:a6:82 +-----BEGIN CERTIFICATE----- +MIIDTzCCAjegAwIBAgIBATANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQDDBJ3ZWIt +cGxhdGZvcm0tdGVzdHMwHhcNMTYxMDE4MDgzNjUzWhcNMjUwMTA0MDgzNjUzWjAd +MRswGQYDVQQDDBJ3ZWItcGxhdGZvcm0tdGVzdHMwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDJ42mFGK0a37psBZYMwSJReDqQQohy9/SMgVlDW+oTDues +J8JyVcZTSE8IN6Ff4Vx4JNMSLStxw1GjrQvGoZUt9c/bUGTI+tAqvK6oK0RToqno +VZK3lU/S89nx4QsXEa1iUMVXTUPt/G8UmVKrJ/RZRfiGd9H8AIqzTH5mGrMEWOWe +V1K4ne/M71luoJks4eEb2hm+YfnDiXYeiB6GZB9bJz5EfK3QBLBX23m9Hn1Kc1LJ +LRZt8IAuB3AJL8qiwxKuLFE91PNO9lPCneBsVm02OLweT9l+Hrkm/i/w1muvAr9N +k0un573r53lpsWUfiPD9zAPOXgCDua1wapV4W5f7AgMBAAGjgZkwgZYwDAYDVR0T +BAUwAwEB/zAdBgNVHQ4EFgQUTmUNO5KzWks0dfspJspTa6FlFX4wRQYDVR0jBD4w +PIAUTmUNO5KzWks0dfspJspTa6FlFX6hIaQfMB0xGzAZBgNVBAMMEndlYi1wbGF0 +Zm9ybS10ZXN0c4IBATALBgNVHQ8EBAMCAgQwEwYDVR0lBAwwCgYIKwYBBQUHAwEw +DQYJKoZIhvcNAQELBQADggEBAMbw2R4kgjq41LOSAOoO1hfpjaB6NbxZuS2wa8In +dGZvJhjCdRRMKXY9KDxuZr48J1UXCfP0LRurtbVc9lwk0X01RTB+QRl5+KE4ys4w +NeDeHHf4/s3dJUwTGNeeizUEWCR+o/WJSPwo5xGI7Lg35S+LYbnzRBJqYsuzFl9h +d4zm1T/sQ5oie9LgOiUVSeR1CaOegmSpP5SGx6iHojRRh8lYiXZCALx80xd6QHmx +9awNnRSl21d/EDYheX6PYpFo0h/J03YYz5gTvyGhCcNAF54WgPRhI0bPeJsAp/pT +ALVn04KEeVDAruAPUVWE6FrNx2qDEd6CWHARuB5nqzAdpoI= +-----END CERTIFICATE-----
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/02.pem b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/02.pem new file mode 100644 index 0000000..bb883ca --- /dev/null +++ b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/02.pem
@@ -0,0 +1,85 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 2 (0x2) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=web-platform-tests + Validity + Not Before: Oct 18 08:36:53 2016 GMT + Not After : Jan 4 08:36:53 2025 GMT + Subject: CN=127.0.0.1 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:aa:bd:55:d1:6f:96:08:8b:8f:72:bd:ce:70:2c: + 08:f5:28:8b:4c:03:ba:78:ad:d3:5e:a9:26:87:5f: + 45:ce:7d:6e:cc:18:f9:73:cb:34:e0:d6:50:bc:47: + 51:61:93:2d:4f:18:c5:61:77:9e:4e:8b:3f:eb:36: + f0:b9:1c:a4:c8:3e:73:0e:c8:fd:9a:32:cc:22:28: + 46:00:c8:03:4a:46:42:bb:0d:06:c0:0e:87:1b:ca: + 91:b8:84:7d:c9:38:41:a8:be:f9:46:cf:9e:7a:f2: + 5c:12:98:9a:eb:34:fe:18:8e:e1:4d:f8:06:a9:43: + 00:2d:bb:8b:ec:ad:f9:3e:9e:82:5b:83:32:f0:47: + 2f:4c:aa:de:e6:83:27:0c:b3:3b:05:d9:ee:ee:1a: + f2:cd:39:d8:15:3a:3a:9d:63:1e:63:ed:77:ab:d9: + 02:b1:63:b1:a0:62:49:4a:43:c5:9f:cc:e3:1d:7f: + 54:92:9f:5e:4a:d7:27:78:32:c1:fa:33:58:66:86: + 0b:44:3a:75:a6:05:2f:41:d3:3b:cb:f6:52:81:d3: + c9:0d:7b:99:22:0e:b9:18:f8:49:b3:99:b3:59:24: + cf:11:90:bb:bb:69:09:ef:9a:f9:8a:32:e0:a8:5c: + 30:97:75:9f:0b:4c:80:b1:a3:40:de:e5:8f:d9:b7: + 23:6f + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Subject Key Identifier: + A1:21:E0:6B:F3:A6:1B:7D:8C:F0:05:33:61:C7:1D:7D:47:6D:66:99 + X509v3 Authority Key Identifier: + keyid:4E:65:0D:3B:92:B3:5A:4B:34:75:FB:29:26:CA:53:6B:A1:65:15:7E + + X509v3 Key Usage: + Digital Signature, Non Repudiation, Key Encipherment + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 Subject Alternative Name: + DNS:127.0.0.1, DNS:www.127.0.0.1, DNS:xn--n8j6ds53lwwkrqhv28a.127.0.0.1, DNS:xn--lve-6lad.127.0.0.1, DNS:www2.127.0.0.1, DNS:www1.127.0.0.1 + Signature Algorithm: sha256WithRSAEncryption + 44:47:be:6b:b8:d7:34:79:ac:e7:a6:5a:5f:e8:2b:7f:c9:a9: + 04:1a:d5:75:a7:d6:3e:f7:98:b3:eb:37:ed:7d:96:37:44:0c: + 7c:ad:e9:8a:c6:50:69:56:63:91:3f:2b:b4:89:0c:f7:03:ae: + 69:b1:b0:d6:00:33:a6:7c:83:db:56:d0:e9:d8:fc:53:be:ad: + b8:3e:3e:c4:e5:22:cd:d0:eb:a7:a5:75:f1:03:ba:bd:49:32: + 0b:1b:b7:2f:56:a8:98:14:43:58:d4:23:05:7c:b9:13:00:85: + 6e:70:e5:5a:45:1f:9e:4a:c0:06:72:93:38:24:9d:d2:d9:82: + 99:c8:5f:c2:c6:f2:f9:53:49:e1:cb:13:72:aa:07:e8:f3:83: + a6:f4:4c:28:a6:94:7c:d7:e4:42:b0:de:d6:ec:0d:e2:af:2f: + cd:44:c4:b6:cb:54:7d:d7:1f:32:4d:97:31:c7:6e:13:14:0e: + a1:f8:6e:e4:b4:ee:15:83:fd:7e:29:e4:7b:06:35:d2:e3:9a: + 6e:79:aa:a1:a2:91:65:98:48:46:1b:33:36:ab:26:c2:76:18: + 94:82:ea:c0:a0:ca:9e:b1:98:5f:98:4d:d7:1c:89:b4:e9:72: + 0d:fa:b7:a9:c2:69:c3:0a:ee:f3:3e:fd:90:87:c8:0c:33:31: + 69:e1:c0:99 +-----BEGIN CERTIFICATE----- +MIIDnTCCAoWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQDDBJ3ZWIt +cGxhdGZvcm0tdGVzdHMwHhcNMTYxMDE4MDgzNjUzWhcNMjUwMTA0MDgzNjUzWjAU +MRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCqvVXRb5YIi49yvc5wLAj1KItMA7p4rdNeqSaHX0XOfW7MGPlzyzTg1lC8 +R1Fhky1PGMVhd55Oiz/rNvC5HKTIPnMOyP2aMswiKEYAyANKRkK7DQbADocbypG4 +hH3JOEGovvlGz5568lwSmJrrNP4YjuFN+AapQwAtu4vsrfk+noJbgzLwRy9Mqt7m +gycMszsF2e7uGvLNOdgVOjqdYx5j7Xer2QKxY7GgYklKQ8WfzOMdf1SSn15K1yd4 +MsH6M1hmhgtEOnWmBS9B0zvL9lKB08kNe5kiDrkY+EmzmbNZJM8RkLu7aQnvmvmK +MuCoXDCXdZ8LTICxo0De5Y/ZtyNvAgMBAAGjgfAwge0wCQYDVR0TBAIwADAdBgNV +HQ4EFgQUoSHga/OmG32M8AUzYccdfUdtZpkwHwYDVR0jBBgwFoAUTmUNO5KzWks0 +dfspJspTa6FlFX4wCwYDVR0PBAQDAgXgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMH4G +A1UdEQR3MHWCCTEyNy4wLjAuMYINd3d3LjEyNy4wLjAuMYIheG4tLW44ajZkczUz +bHd3a3JxaHYyOGEuMTI3LjAuMC4xghZ4bi0tbHZlLTZsYWQuMTI3LjAuMC4xgg53 +d3cyLjEyNy4wLjAuMYIOd3d3MS4xMjcuMC4wLjEwDQYJKoZIhvcNAQELBQADggEB +AERHvmu41zR5rOemWl/oK3/JqQQa1XWn1j73mLPrN+19ljdEDHyt6YrGUGlWY5E/ +K7SJDPcDrmmxsNYAM6Z8g9tW0OnY/FO+rbg+PsTlIs3Q66eldfEDur1JMgsbty9W +qJgUQ1jUIwV8uRMAhW5w5VpFH55KwAZykzgkndLZgpnIX8LG8vlTSeHLE3KqB+jz +g6b0TCimlHzX5EKw3tbsDeKvL81ExLbLVH3XHzJNlzHHbhMUDqH4buS07hWD/X4p +5HsGNdLjmm55qqGikWWYSEYbMzarJsJ2GJSC6sCgyp6xmF+YTdccibTpcg36t6nC +acMK7vM+/ZCHyAwzMWnhwJk= +-----END CERTIFICATE-----
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/127.0.0.1.key b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/127.0.0.1.key new file mode 100644 index 0000000..d11d407 --- /dev/null +++ b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/127.0.0.1.key
@@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCqvVXRb5YIi49y +vc5wLAj1KItMA7p4rdNeqSaHX0XOfW7MGPlzyzTg1lC8R1Fhky1PGMVhd55Oiz/r +NvC5HKTIPnMOyP2aMswiKEYAyANKRkK7DQbADocbypG4hH3JOEGovvlGz5568lwS +mJrrNP4YjuFN+AapQwAtu4vsrfk+noJbgzLwRy9Mqt7mgycMszsF2e7uGvLNOdgV +OjqdYx5j7Xer2QKxY7GgYklKQ8WfzOMdf1SSn15K1yd4MsH6M1hmhgtEOnWmBS9B +0zvL9lKB08kNe5kiDrkY+EmzmbNZJM8RkLu7aQnvmvmKMuCoXDCXdZ8LTICxo0De +5Y/ZtyNvAgMBAAECggEAMRSCEdVIxKYRk0M6j4YpAQgpmq1LshsNsp1fJPTfJS9r +ZSIcuwuD5MnWpXi+zRS4po8RViZDjIJ82kmNwziXqbRB8NMYC3ZktcDr9peIUpaF +4i0pbpX+gp+laew0GF6iiTZ0g8V04hS/IN1bUxeFn1ubKoWMrKVE7OCHpX6RMk13 +v+ffdWVSjeKGufFUlN6OTCdnwgXsZrJPmj1CWWnyC2kzdKhLk0slEuYRpG5Yhzz2 +oKdivyGcoYmtnPSJa6CJEy6EPEwbecryMPuBwhBJIKQb1EvoWHzxoE9IAfLaGUFD +xJlT9IbVTR5xZ0MQuXg6zzOoFoa2hFHI+foCFrmDuQKBgQDjQJ0O8SFHLRHEXmwK +adJcI+2wJHWUtK6ArsM1dPrUZ4sDHbPNPxpZ48WWjEK6vDNfbaYpUHmvV37+kJkM +P4uunqxkID9nEsctAJRjnKmMf41k0/YwokPraXY/57DsUpWZK39q09c34PqPneBW +4KEy4j21MRJp9kBgy9QHkHcYRQKBgQDAVpmQBjQiifvi2g6t/kZndEdRnbC/zgNy +82/1pNKdiewqlF9WsLem4KXSkvRuwlBeF4D1W6mDCwMNBn/YYN/GidmUS51Z96T1 +QFbRliBA/SKbEE9NZc5xlzLkgXHEDFfj3WIRIlgccWu//DcI2w3rfjRZag3GaG+X +JX7CLZ2qIwKBgQCeNq7h5zjO7+7NsxsvCMuewJjLqCaAWGahSoq3nfC/jjL3AWfb +vlIfQPegP5h5n2t4xcMIQnHlhFny60LShy89bFUDBHx/y1AF4cBttXVJTshm6Tce +VupIbE0aYrkHXtuuHt1/x2qwCZ8H+9djRNKVtXNWwYpbYUki2uWMOqTfrQKBgQCk +WtwKKGLwiLyHUxJsN/ZtfP1cjsV0gVSNK9ymKdwX5r26fOMjLwsgPKM2V4EsDDuk +y6zU/SjS49wNi1o/yjgubalRgXPKZ0W8lOgbXI/fOPATVVKOrspEYpGIldxjTLDl +9E1Smuh0Fa+fdKSKmrVAYK3XIjr3KlMA83dn8pbhxQKBgFGI8qh93jkhS+pWSj3u +vJU7eML8DggzWMsY+lmTHWVAWM0o9LLcr3+LghgFdrerK2RjmGCZQ96YN+eB/XoI +NQqQj39X5Yst5KhwQc+Iv1d2Wyu6cDECoXFJFL3eW0Cd4YQeaAMvAi6mpfNRAQWy +UJOa4xQbckFK0V8JXHoVCF9P +-----END PRIVATE KEY-----
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/127.0.0.1.pem b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/127.0.0.1.pem new file mode 100644 index 0000000..bb883ca --- /dev/null +++ b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/127.0.0.1.pem
@@ -0,0 +1,85 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 2 (0x2) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=web-platform-tests + Validity + Not Before: Oct 18 08:36:53 2016 GMT + Not After : Jan 4 08:36:53 2025 GMT + Subject: CN=127.0.0.1 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:aa:bd:55:d1:6f:96:08:8b:8f:72:bd:ce:70:2c: + 08:f5:28:8b:4c:03:ba:78:ad:d3:5e:a9:26:87:5f: + 45:ce:7d:6e:cc:18:f9:73:cb:34:e0:d6:50:bc:47: + 51:61:93:2d:4f:18:c5:61:77:9e:4e:8b:3f:eb:36: + f0:b9:1c:a4:c8:3e:73:0e:c8:fd:9a:32:cc:22:28: + 46:00:c8:03:4a:46:42:bb:0d:06:c0:0e:87:1b:ca: + 91:b8:84:7d:c9:38:41:a8:be:f9:46:cf:9e:7a:f2: + 5c:12:98:9a:eb:34:fe:18:8e:e1:4d:f8:06:a9:43: + 00:2d:bb:8b:ec:ad:f9:3e:9e:82:5b:83:32:f0:47: + 2f:4c:aa:de:e6:83:27:0c:b3:3b:05:d9:ee:ee:1a: + f2:cd:39:d8:15:3a:3a:9d:63:1e:63:ed:77:ab:d9: + 02:b1:63:b1:a0:62:49:4a:43:c5:9f:cc:e3:1d:7f: + 54:92:9f:5e:4a:d7:27:78:32:c1:fa:33:58:66:86: + 0b:44:3a:75:a6:05:2f:41:d3:3b:cb:f6:52:81:d3: + c9:0d:7b:99:22:0e:b9:18:f8:49:b3:99:b3:59:24: + cf:11:90:bb:bb:69:09:ef:9a:f9:8a:32:e0:a8:5c: + 30:97:75:9f:0b:4c:80:b1:a3:40:de:e5:8f:d9:b7: + 23:6f + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Subject Key Identifier: + A1:21:E0:6B:F3:A6:1B:7D:8C:F0:05:33:61:C7:1D:7D:47:6D:66:99 + X509v3 Authority Key Identifier: + keyid:4E:65:0D:3B:92:B3:5A:4B:34:75:FB:29:26:CA:53:6B:A1:65:15:7E + + X509v3 Key Usage: + Digital Signature, Non Repudiation, Key Encipherment + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 Subject Alternative Name: + DNS:127.0.0.1, DNS:www.127.0.0.1, DNS:xn--n8j6ds53lwwkrqhv28a.127.0.0.1, DNS:xn--lve-6lad.127.0.0.1, DNS:www2.127.0.0.1, DNS:www1.127.0.0.1 + Signature Algorithm: sha256WithRSAEncryption + 44:47:be:6b:b8:d7:34:79:ac:e7:a6:5a:5f:e8:2b:7f:c9:a9: + 04:1a:d5:75:a7:d6:3e:f7:98:b3:eb:37:ed:7d:96:37:44:0c: + 7c:ad:e9:8a:c6:50:69:56:63:91:3f:2b:b4:89:0c:f7:03:ae: + 69:b1:b0:d6:00:33:a6:7c:83:db:56:d0:e9:d8:fc:53:be:ad: + b8:3e:3e:c4:e5:22:cd:d0:eb:a7:a5:75:f1:03:ba:bd:49:32: + 0b:1b:b7:2f:56:a8:98:14:43:58:d4:23:05:7c:b9:13:00:85: + 6e:70:e5:5a:45:1f:9e:4a:c0:06:72:93:38:24:9d:d2:d9:82: + 99:c8:5f:c2:c6:f2:f9:53:49:e1:cb:13:72:aa:07:e8:f3:83: + a6:f4:4c:28:a6:94:7c:d7:e4:42:b0:de:d6:ec:0d:e2:af:2f: + cd:44:c4:b6:cb:54:7d:d7:1f:32:4d:97:31:c7:6e:13:14:0e: + a1:f8:6e:e4:b4:ee:15:83:fd:7e:29:e4:7b:06:35:d2:e3:9a: + 6e:79:aa:a1:a2:91:65:98:48:46:1b:33:36:ab:26:c2:76:18: + 94:82:ea:c0:a0:ca:9e:b1:98:5f:98:4d:d7:1c:89:b4:e9:72: + 0d:fa:b7:a9:c2:69:c3:0a:ee:f3:3e:fd:90:87:c8:0c:33:31: + 69:e1:c0:99 +-----BEGIN CERTIFICATE----- +MIIDnTCCAoWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQDDBJ3ZWIt +cGxhdGZvcm0tdGVzdHMwHhcNMTYxMDE4MDgzNjUzWhcNMjUwMTA0MDgzNjUzWjAU +MRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCqvVXRb5YIi49yvc5wLAj1KItMA7p4rdNeqSaHX0XOfW7MGPlzyzTg1lC8 +R1Fhky1PGMVhd55Oiz/rNvC5HKTIPnMOyP2aMswiKEYAyANKRkK7DQbADocbypG4 +hH3JOEGovvlGz5568lwSmJrrNP4YjuFN+AapQwAtu4vsrfk+noJbgzLwRy9Mqt7m +gycMszsF2e7uGvLNOdgVOjqdYx5j7Xer2QKxY7GgYklKQ8WfzOMdf1SSn15K1yd4 +MsH6M1hmhgtEOnWmBS9B0zvL9lKB08kNe5kiDrkY+EmzmbNZJM8RkLu7aQnvmvmK +MuCoXDCXdZ8LTICxo0De5Y/ZtyNvAgMBAAGjgfAwge0wCQYDVR0TBAIwADAdBgNV +HQ4EFgQUoSHga/OmG32M8AUzYccdfUdtZpkwHwYDVR0jBBgwFoAUTmUNO5KzWks0 +dfspJspTa6FlFX4wCwYDVR0PBAQDAgXgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMH4G +A1UdEQR3MHWCCTEyNy4wLjAuMYINd3d3LjEyNy4wLjAuMYIheG4tLW44ajZkczUz +bHd3a3JxaHYyOGEuMTI3LjAuMC4xghZ4bi0tbHZlLTZsYWQuMTI3LjAuMC4xgg53 +d3cyLjEyNy4wLjAuMYIOd3d3MS4xMjcuMC4wLjEwDQYJKoZIhvcNAQELBQADggEB +AERHvmu41zR5rOemWl/oK3/JqQQa1XWn1j73mLPrN+19ljdEDHyt6YrGUGlWY5E/ +K7SJDPcDrmmxsNYAM6Z8g9tW0OnY/FO+rbg+PsTlIs3Q66eldfEDur1JMgsbty9W +qJgUQ1jUIwV8uRMAhW5w5VpFH55KwAZykzgkndLZgpnIX8LG8vlTSeHLE3KqB+jz +g6b0TCimlHzX5EKw3tbsDeKvL81ExLbLVH3XHzJNlzHHbhMUDqH4buS07hWD/X4p +5HsGNdLjmm55qqGikWWYSEYbMzarJsJ2GJSC6sCgyp6xmF+YTdccibTpcg36t6nC +acMK7vM+/ZCHyAwzMWnhwJk= +-----END CERTIFICATE-----
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/cacert.pem b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/cacert.pem new file mode 100644 index 0000000..ff31326 --- /dev/null +++ b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/cacert.pem
@@ -0,0 +1,83 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=web-platform-tests + Validity + Not Before: Oct 18 08:36:53 2016 GMT + Not After : Jan 4 08:36:53 2025 GMT + Subject: CN=web-platform-tests + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c9:e3:69:85:18:ad:1a:df:ba:6c:05:96:0c:c1: + 22:51:78:3a:90:42:88:72:f7:f4:8c:81:59:43:5b: + ea:13:0e:e7:ac:27:c2:72:55:c6:53:48:4f:08:37: + a1:5f:e1:5c:78:24:d3:12:2d:2b:71:c3:51:a3:ad: + 0b:c6:a1:95:2d:f5:cf:db:50:64:c8:fa:d0:2a:bc: + ae:a8:2b:44:53:a2:a9:e8:55:92:b7:95:4f:d2:f3: + d9:f1:e1:0b:17:11:ad:62:50:c5:57:4d:43:ed:fc: + 6f:14:99:52:ab:27:f4:59:45:f8:86:77:d1:fc:00: + 8a:b3:4c:7e:66:1a:b3:04:58:e5:9e:57:52:b8:9d: + ef:cc:ef:59:6e:a0:99:2c:e1:e1:1b:da:19:be:61: + f9:c3:89:76:1e:88:1e:86:64:1f:5b:27:3e:44:7c: + ad:d0:04:b0:57:db:79:bd:1e:7d:4a:73:52:c9:2d: + 16:6d:f0:80:2e:07:70:09:2f:ca:a2:c3:12:ae:2c: + 51:3d:d4:f3:4e:f6:53:c2:9d:e0:6c:56:6d:36:38: + bc:1e:4f:d9:7e:1e:b9:26:fe:2f:f0:d6:6b:af:02: + bf:4d:93:4b:a7:e7:bd:eb:e7:79:69:b1:65:1f:88: + f0:fd:cc:03:ce:5e:00:83:b9:ad:70:6a:95:78:5b: + 97:fb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:TRUE + X509v3 Subject Key Identifier: + 4E:65:0D:3B:92:B3:5A:4B:34:75:FB:29:26:CA:53:6B:A1:65:15:7E + X509v3 Authority Key Identifier: + keyid:4E:65:0D:3B:92:B3:5A:4B:34:75:FB:29:26:CA:53:6B:A1:65:15:7E + DirName:/CN=web-platform-tests + serial:01 + + X509v3 Key Usage: + Certificate Sign + X509v3 Extended Key Usage: + TLS Web Server Authentication + Signature Algorithm: sha256WithRSAEncryption + c6:f0:d9:1e:24:82:3a:b8:d4:b3:92:00:ea:0e:d6:17:e9:8d: + a0:7a:35:bc:59:b9:2d:b0:6b:c2:27:74:66:6f:26:18:c2:75: + 14:4c:29:76:3d:28:3c:6e:66:be:3c:27:55:17:09:f3:f4:2d: + 1b:ab:b5:b5:5c:f6:5c:24:d1:7d:35:45:30:7e:41:19:79:f8: + a1:38:ca:ce:30:35:e0:de:1c:77:f8:fe:cd:dd:25:4c:13:18: + d7:9e:8b:35:04:58:24:7e:a3:f5:89:48:fc:28:e7:11:88:ec: + b8:37:e5:2f:8b:61:b9:f3:44:12:6a:62:cb:b3:16:5f:61:77: + 8c:e6:d5:3f:ec:43:9a:22:7b:d2:e0:3a:25:15:49:e4:75:09: + a3:9e:82:64:a9:3f:94:86:c7:a8:87:a2:34:51:87:c9:58:89: + 76:42:00:bc:7c:d3:17:7a:40:79:b1:f5:ac:0d:9d:14:a5:db: + 57:7f:10:36:21:79:7e:8f:62:91:68:d2:1f:c9:d3:76:18:cf: + 98:13:bf:21:a1:09:c3:40:17:9e:16:80:f4:61:23:46:cf:78: + 9b:00:a7:fa:53:00:b5:67:d3:82:84:79:50:c0:ae:e0:0f:51: + 55:84:e8:5a:cd:c7:6a:83:11:de:82:58:70:11:b8:1e:67:ab: + 30:1d:a6:82 +-----BEGIN CERTIFICATE----- +MIIDTzCCAjegAwIBAgIBATANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQDDBJ3ZWIt +cGxhdGZvcm0tdGVzdHMwHhcNMTYxMDE4MDgzNjUzWhcNMjUwMTA0MDgzNjUzWjAd +MRswGQYDVQQDDBJ3ZWItcGxhdGZvcm0tdGVzdHMwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDJ42mFGK0a37psBZYMwSJReDqQQohy9/SMgVlDW+oTDues +J8JyVcZTSE8IN6Ff4Vx4JNMSLStxw1GjrQvGoZUt9c/bUGTI+tAqvK6oK0RToqno +VZK3lU/S89nx4QsXEa1iUMVXTUPt/G8UmVKrJ/RZRfiGd9H8AIqzTH5mGrMEWOWe +V1K4ne/M71luoJks4eEb2hm+YfnDiXYeiB6GZB9bJz5EfK3QBLBX23m9Hn1Kc1LJ +LRZt8IAuB3AJL8qiwxKuLFE91PNO9lPCneBsVm02OLweT9l+Hrkm/i/w1muvAr9N +k0un573r53lpsWUfiPD9zAPOXgCDua1wapV4W5f7AgMBAAGjgZkwgZYwDAYDVR0T +BAUwAwEB/zAdBgNVHQ4EFgQUTmUNO5KzWks0dfspJspTa6FlFX4wRQYDVR0jBD4w +PIAUTmUNO5KzWks0dfspJspTa6FlFX6hIaQfMB0xGzAZBgNVBAMMEndlYi1wbGF0 +Zm9ybS10ZXN0c4IBATALBgNVHQ8EBAMCAgQwEwYDVR0lBAwwCgYIKwYBBQUHAwEw +DQYJKoZIhvcNAQELBQADggEBAMbw2R4kgjq41LOSAOoO1hfpjaB6NbxZuS2wa8In +dGZvJhjCdRRMKXY9KDxuZr48J1UXCfP0LRurtbVc9lwk0X01RTB+QRl5+KE4ys4w +NeDeHHf4/s3dJUwTGNeeizUEWCR+o/WJSPwo5xGI7Lg35S+LYbnzRBJqYsuzFl9h +d4zm1T/sQ5oie9LgOiUVSeR1CaOegmSpP5SGx6iHojRRh8lYiXZCALx80xd6QHmx +9awNnRSl21d/EDYheX6PYpFo0h/J03YYz5gTvyGhCcNAF54WgPRhI0bPeJsAp/pT +ALVn04KEeVDAruAPUVWE6FrNx2qDEd6CWHARuB5nqzAdpoI= +-----END CERTIFICATE-----
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/cakey.pem b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/cakey.pem new file mode 100644 index 0000000..7de82894 --- /dev/null +++ b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/cakey.pem
@@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIkWIO4EtVGkYCAggA +MBQGCCqGSIb3DQMHBAi3OesMu9lDwgSCBMjDYQ4+dIkQmN4JbaHXINc8O3DU78J/ +ToUEkMz7t3G2aVRJk7C3Rbzk3u8OMs1p+3CaoH7fpF86kvDjeHwGygRPSZ/ZEh9W +Si4zMd1xFVlfdbGf1/9n4nEGeorK8rGShXnhs/x1v9ZAmkl3xl9UUkNeRqfmgfor +FefDcqWaLbGfyq2GVrUk6P2F9klfitdWvqoatKkUjLyO04r4cr7orpLI60gbO7tb +Huis1C8rmcbAkI5alHZuvlJrAcFacV4oO9SXrIgYiYiLb+8vyGz2Ol6YaFUN+KXq +K467P0K6y2BxWB6N8UbeQSwgyOA5ckS0kdFeDsBv8WR8eXpDPhLETYEDoNJcUpN2 +Je1hrRUgCDsllSK92NQt2atuXKdUBJCNQ+Y5X/8HsLDqDynm3ea1Sc5gj9a/H0OQ +yGj5S+xSRQcLTHKANwvOoT1jgrnA5lJSYrbYMY9Wqdt2bJMgxbXsf3CRY0p2uf9m +9AkYZzDZVocV1mGgtrSSIcFFvEYI8D10z6rRIhTcx/4k0mmyMWxVTW35JHyLvLQd +nIRbGXYz9zgUYRiOGUDLbrYYZud13rE+62Y8AxVHbrUXMSala3jFN+Ly5JXnLL1P +5Cy6Hab1BZ4WOBUHEOP5GhKmnrWGkRBSPhQVlOXW5ymTZoSwBgslovZZAhZU4yp0 +RvTldjygC499hTdAfQfrQYok0RnCfVqKIZkng8O5j6IbtPn7dyq4Zv/6sNdNwzPD +EkkkbPpzvbrboOVf4dmLo8PBIRgJTKZoBBj/RSEVLwx2Zv9azJ0OuQEIY62a9039 +Ithl7HbQYSr8B0JJheL/dYdeICiz9sU97nAf/I9oXGeVj/rX5KwEbSIi10TFTKkA +9v+tbmeHj8B/87xHexZ8NIMXDTYq2TY4PFlUjFIClNofVPrcJidpBfdyF5RPqfrw +tH53Cc2sxN+woefPNQPoQHw8WUQ0MgxdN1XwuizvZKM3+44wqhsQloSNrzngjAJ4 +Y4A+oNkTqCuk0uonln3G8Ay2W5A0MKNSSmPXqUeGeANn2Lqj7UCVBeFwx0kjwKGF +Bn1+xff/Uk09sy8SBlQRqoFkYak4QIrA1AznzQz860zHLmfoH0el8GSQQ26F2xT2 +ICZ0ma52tcAleTH3z6qLcAqh4ykKEMVoIun/ZwVYpqsucntCNQh35M5EUjb+9Tl7 +hGg3Ez8IZ2lkYlpYPzYEd5+vrl4AP/tGWOSmvb9LziulCn0WkZyroTCN7q66WEyo +fqRIgk+5//NX3CSNKc+bVjNIxQvgh1xQX1zKwcWVbwLykTTkqIaiDsqDVteyOawh +s1Zb253GSul0mCgixZG0nGciIYZpK7ky6PWC1SbQEKh8VlYgusqgJ7cMtAlb4Cgy +p2WM/my+FN9DbzUYlgzE8XtWoYbf5NPk7Dx/iOp2mePMunhjc6TaM9T4fAsec7sf +R8QHIeaQiP2P8ngnhgRe2iSWN1PzAq4ecs/WJ7w+Gpto+QWXy6nzEfcrL55uvIl3 +G2BANBvZnOoymYuZHGArlHT4xFISz3dFPuYzk56ux1riLMB2GKgjoSwU/Mkx5+Df +dN/ECIt28jcJIWUHhg91FaWXLvmp1peGydi2CHyPzWR43szB2Z/mkrzz6vVomBXc +Blo= +-----END ENCRYPTED PRIVATE KEY-----
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/index.txt b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/index.txt new file mode 100644 index 0000000..c51d519 --- /dev/null +++ b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/index.txt
@@ -0,0 +1,2 @@ +V 250104083653Z 01 unknown /CN=web-platform-tests +V 250104083653Z 02 unknown /CN=127.0.0.1
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/index.txt.attr b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/index.txt.attr new file mode 100644 index 0000000..8f7e63a --- /dev/null +++ b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/index.txt.attr
@@ -0,0 +1 @@ +unique_subject = yes
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/index.txt.attr.old b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/index.txt.attr.old new file mode 100644 index 0000000..8f7e63a --- /dev/null +++ b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/index.txt.attr.old
@@ -0,0 +1 @@ +unique_subject = yes
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/index.txt.old b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/index.txt.old new file mode 100644 index 0000000..d9c0e58 --- /dev/null +++ b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/index.txt.old
@@ -0,0 +1 @@ +V 250104083653Z 01 unknown /CN=web-platform-tests
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/serial b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/serial new file mode 100644 index 0000000..75016ea3 --- /dev/null +++ b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/serial
@@ -0,0 +1 @@ +03
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/serial.old b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/serial.old new file mode 100644 index 0000000..9e22bcb8 --- /dev/null +++ b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/certs/serial.old
@@ -0,0 +1 @@ +02
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/wpt.config.json b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/wpt.config.json index 84fd3f4c..6243954c 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/wpt.config.json +++ b/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/wpt/wpt.config.json
@@ -9,5 +9,13 @@ "https": [8444], "ws": [9001], "wss": [9444] + }, + "ssl": { + "type": "pregenerated", + "encrypt_after_connect": false, + "pregenerated": { + "host_key_path": "../certs/127.0.0.1.key", + "host_cert_path": "../certs/127.0.0.1.pem" + } } }
diff --git a/third_party/WebKit/public/platform/WebContentDecryptionModuleException.h b/third_party/WebKit/public/platform/WebContentDecryptionModuleException.h index ca278ae7..8efa194 100644 --- a/third_party/WebKit/public/platform/WebContentDecryptionModuleException.h +++ b/third_party/WebKit/public/platform/WebContentDecryptionModuleException.h
@@ -7,14 +7,16 @@ namespace blink { +// From https://w3c.github.io/encrypted-media/#exceptions. enum WebContentDecryptionModuleException { + WebContentDecryptionModuleExceptionTypeError, WebContentDecryptionModuleExceptionNotSupportedError, WebContentDecryptionModuleExceptionInvalidStateError, - WebContentDecryptionModuleExceptionInvalidAccessError, WebContentDecryptionModuleExceptionQuotaExceededError, - WebContentDecryptionModuleExceptionUnknownError, - WebContentDecryptionModuleExceptionClientError, - WebContentDecryptionModuleExceptionOutputError, + // TODO(jrummell): UnknownError is not part of the spec, but CDMs can + // generate other error codes (in addition to the 4 listed above). Remove + // UnknownError when the CDMs no longer use other error codes. + WebContentDecryptionModuleExceptionUnknownError }; } // namespace blink
diff --git a/third_party/WebKit/public/platform/modules/notifications/WebNotificationDelegate.h b/third_party/WebKit/public/platform/modules/notifications/WebNotificationDelegate.h index 2ca0846d..319d0d3 100644 --- a/third_party/WebKit/public/platform/modules/notifications/WebNotificationDelegate.h +++ b/third_party/WebKit/public/platform/modules/notifications/WebNotificationDelegate.h
@@ -7,16 +7,15 @@ namespace blink { -class WebString; - // A delegate through which the embedder can trigger events on a Document-bound // Web Notifications object. Service Worker-bound Web Notifications will not // have a delegate, as their events will be fired on a Service Worker instead. class WebNotificationDelegate { public: - virtual void didShowNotification(const WebString& notificationId) = 0; - virtual void didClickNotification() = 0; - virtual void didCloseNotification() = 0; + virtual void dispatchClickEvent() = 0; + virtual void dispatchShowEvent() = 0; + virtual void dispatchErrorEvent() = 0; + virtual void dispatchCloseEvent() = 0; }; } // namespace blink
diff --git a/third_party/WebKit/public/platform/modules/notifications/WebNotificationManager.h b/third_party/WebKit/public/platform/modules/notifications/WebNotificationManager.h index 3496f6e0..20aa454a 100644 --- a/third_party/WebKit/public/platform/modules/notifications/WebNotificationManager.h +++ b/third_party/WebKit/public/platform/modules/notifications/WebNotificationManager.h
@@ -59,10 +59,13 @@ WebServiceWorkerRegistration*, WebNotificationGetCallbacks*) = 0; - // Closes a notification identified by its notification Id. - virtual void close(const WebSecurityOrigin&, - const WebString& tag, - const WebString& notificationId) = 0; + // Closes a notification previously shown, and removes it if being shown. + virtual void close(WebNotificationDelegate*) = 0; + + // Closes a persistent notification identified by its notification Id. + virtual void closePersistent(const WebSecurityOrigin&, + const WebString& tag, + const WebString& notificationId) = 0; // Indicates that the delegate object is being destroyed, and must no longer // be used by the embedder to dispatch events.
diff --git a/third_party/WebKit/public/web/WebSettings.h b/third_party/WebKit/public/web/WebSettings.h index d390ab1b..764fc47 100644 --- a/third_party/WebKit/public/web/WebSettings.h +++ b/third_party/WebKit/public/web/WebSettings.h
@@ -214,7 +214,6 @@ virtual void setPerTilePaintingEnabled(bool) = 0; virtual void setPictographFontFamily(const WebString&, UScriptCode = USCRIPT_COMMON) = 0; - virtual void setPinchOverlayScrollbarThickness(int) = 0; virtual void setPluginsEnabled(bool) = 0; virtual void setAvailablePointerTypes(int) = 0; virtual void setPrimaryPointerType(PointerType) = 0;
diff --git a/third_party/closure_compiler/README.chromium b/third_party/closure_compiler/README.chromium index fc7701c..65f0c48 100644 --- a/third_party/closure_compiler/README.chromium +++ b/third_party/closure_compiler/README.chromium
@@ -3,7 +3,7 @@ URL: http://github.com/google/closure-compiler Version: v20150729-236-gad656a1 Date: 2015/08/26 08:46 -Revision: c64418fd5518bb60aeb6a627d2bcc46a3d4be788 +Revision: 6466a369c2e0ba3a7eede1e0347f89fd011ad1cf License: Apache 2.0 License File: LICENSE Security Critical: no
diff --git a/third_party/closure_compiler/compiler/compiler.jar b/third_party/closure_compiler/compiler/compiler.jar index 2b95321..2782453a 100644 --- a/third_party/closure_compiler/compiler/compiler.jar +++ b/third_party/closure_compiler/compiler/compiler.jar Binary files differ
diff --git a/third_party/closure_compiler/externs/chrome_extensions.js b/third_party/closure_compiler/externs/chrome_extensions.js index 6c046f6..57d3ebd 100644 --- a/third_party/closure_compiler/externs/chrome_extensions.js +++ b/third_party/closure_compiler/externs/chrome_extensions.js
@@ -321,12 +321,14 @@ /** * @see http://developer.chrome.com/apps/app.window.html#type-AppWindow + * @return {undefined} */ chrome.app.window.AppWindow.prototype.focus = function() {}; /** * @see http://developer.chrome.com/apps/app.window.html#type-AppWindow + * @return {undefined} */ chrome.app.window.AppWindow.prototype.fullscreen = function() {}; @@ -340,6 +342,7 @@ /** * @see http://developer.chrome.com/apps/app.window.html#type-AppWindow + * @return {undefined} */ chrome.app.window.AppWindow.prototype.minimize = function() {}; @@ -353,6 +356,7 @@ /** * @see http://developer.chrome.com/apps/app.window.html#type-AppWindow + * @return {undefined} */ chrome.app.window.AppWindow.prototype.maximize = function() {}; @@ -366,6 +370,7 @@ /** * @see http://developer.chrome.com/apps/app.window.html#type-AppWindow + * @return {undefined} */ chrome.app.window.AppWindow.prototype.restore = function() {}; @@ -374,6 +379,7 @@ * @param {number} left The new left position, in pixels. * @param {number} top The new top position, in pixels. * @see http://developer.chrome.com/apps/app.window.html#type-AppWindow + * @return {undefined} */ chrome.app.window.AppWindow.prototype.moveTo = function(left, top) {}; @@ -382,24 +388,28 @@ * @param {number} width The new width, in pixels. * @param {number} height The new height, in pixels. * @see http://developer.chrome.com/apps/app.window.html#type-AppWindow + * @return {undefined} */ chrome.app.window.AppWindow.prototype.resizeTo = function(width, height) {}; /** * @see http://developer.chrome.com/apps/app.window.html#type-AppWindow + * @return {undefined} */ chrome.app.window.AppWindow.prototype.drawAttention = function() {}; /** * @see http://developer.chrome.com/apps/app.window.html#type-AppWindow + * @return {undefined} */ chrome.app.window.AppWindow.prototype.clearAttention = function() {}; /** * @see http://developer.chrome.com/apps/app.window.html#type-AppWindow + * @return {undefined} */ chrome.app.window.AppWindow.prototype.close = function() {}; @@ -407,12 +417,14 @@ /** * @param {boolean=} opt_focus Should the window be focused? Defaults to true. * @see http://developer.chrome.com/apps/app.window.html#type-AppWindow + * @return {undefined} */ chrome.app.window.AppWindow.prototype.show = function(opt_focus) {}; /** * @see http://developer.chrome.com/apps/app.window.html#type-AppWindow + * @return {undefined} */ chrome.app.window.AppWindow.prototype.hide = function() {}; @@ -427,6 +439,7 @@ /** * @param {!chrome.app.window.ContentBounds} bounds The new window bounds. * @see http://developer.chrome.com/apps/app.window.html#type-AppWindow + * @return {undefined} */ chrome.app.window.AppWindow.prototype.setBounds = function(bounds) {}; @@ -442,6 +455,7 @@ * @param {boolean} alwaysOnTop Set whether the window should stay above most * other windows. * @see http://developer.chrome.com/apps/app.window.html#type-AppWindow + * @return {undefined} */ chrome.app.window.AppWindow.prototype.setAlwaysOnTop = function(alwaysOnTop) {}; @@ -450,6 +464,7 @@ * @param {boolean} alwaysVisible Set whether the window is visible on all * workspaces. * @see http://developer.chrome.com/apps/app.window.html#type-AppWindow + * @return {undefined} */ chrome.app.window.AppWindow.prototype.setVisibleOnAllWorkspaces = function(alwaysVisible) {}; @@ -459,6 +474,7 @@ * @param {boolean} wantAllKeys Set whether the window should get all keyboard * events including system keys that are usually not sent. * @see http://developer.chrome.com/apps/app.window.html#type-AppWindow + * @return {undefined} */ chrome.app.window.AppWindow.prototype.setInterceptAllKeys = function(wantAllKeys) {}; @@ -595,6 +611,7 @@ * @param {function(!chrome.app.window.AppWindow)=} opt_createWindowCallback * Callback to be run. * @see http://developer.chrome.com/apps/app.window.html#method-create + * @return {undefined} */ chrome.app.window.create = function( url, opt_options, opt_createWindowCallback) {}; @@ -697,6 +714,7 @@ * @param {!chrome.audioModem.RequestParams} params * @param {!ArrayBuffer} token * @param {function(string)} callback + * @return {undefined} */ chrome.audioModem.transmit = function(params, token, callback) {}; @@ -704,6 +722,7 @@ /** * @param {string} band * @param {function(string)} callback + * @return {undefined} */ chrome.audioModem.stopTransmit = function(band, callback) {}; @@ -711,6 +730,7 @@ /** * @param {!chrome.audioModem.RequestParams} params * @param {function(string)} callback + * @return {undefined} */ chrome.audioModem.receive = function(params, callback) {}; @@ -718,6 +738,7 @@ /** * @param {string} band * @param {function(string)} callback + * @return {undefined} */ chrome.audioModem.stopReceive = function(band, callback) {}; @@ -728,12 +749,14 @@ /** * @param {function(!Array<!chrome.audioModem.ReceivedToken>)} callback + * @return {undefined} */ chrome.audioModem.ReceivedEvent.prototype.addListener = function(callback) {}; /** * @param {function(!Array<!chrome.audioModem.ReceivedToken>)} callback + * @return {undefined} */ chrome.audioModem.ReceivedEvent.prototype.removeListener = function(callback) {}; @@ -842,13 +865,30 @@ chrome.bluetooth.Device.prototype.connected; +/** @type {boolean|undefined} */ +chrome.bluetooth.Device.prototype.connecting; + + +/** @type {boolean|undefined} */ +chrome.bluetooth.Device.prototype.connectable; + + /** @type {!Array<string>|undefined} */ chrome.bluetooth.Device.prototype.uuids; +/** @type {number|undefined} */ +chrome.bluetooth.Device.prototype.inquiryRssi; + + +/** @type {number|undefined} */ +chrome.bluetooth.Device.prototype.inquiryTxPower; + + /** * @param {function(!chrome.bluetooth.AdapterState)} callback * @see https://developer.chrome.com/apps/bluetooth#method-getAdapterState + * @return {undefined} */ chrome.bluetooth.getAdapterState = function(callback) {}; @@ -857,6 +897,7 @@ * @param {string} deviceAddress * @param {function(!chrome.bluetooth.Device)} callback * @see https://developer.chrome.com/apps/bluetooth#method-getDevice + * @return {undefined} */ chrome.bluetooth.getDevice = function(deviceAddress, callback) {}; @@ -864,6 +905,7 @@ /** * @param {function(!Array<!chrome.bluetooth.Device>)} callback * @see https://developer.chrome.com/apps/bluetooth#method-getDevices + * @return {undefined} */ chrome.bluetooth.getDevices = function(callback) {}; @@ -871,6 +913,7 @@ /** * @param {function()=} opt_callback * @see https://developer.chrome.com/apps/bluetooth#method-startDiscovery + * @return {undefined} */ chrome.bluetooth.startDiscovery = function(opt_callback) {}; @@ -878,6 +921,7 @@ /** * @param {function()=} opt_callback * @see https://developer.chrome.com/apps/bluetooth#method-stopDiscovery + * @return {undefined} */ chrome.bluetooth.stopDiscovery = function(opt_callback) {}; @@ -890,12 +934,18 @@ chrome.bluetooth.AdapterStateEvent = function() {}; -/** @param {function(!chrome.bluetooth.AdapterState): void} callback */ +/** + * @param {function(!chrome.bluetooth.AdapterState): void} callback + * @return {undefined} + */ chrome.bluetooth.AdapterStateEvent.prototype.addListener = function(callback) {}; -/** @param {function(!chrome.bluetooth.AdapterState): void} callback */ +/** + * @param {function(!chrome.bluetooth.AdapterState): void} callback + * @return {undefined} + */ chrome.bluetooth.AdapterStateEvent.prototype.removeListener = function(callback) {}; @@ -927,11 +977,17 @@ chrome.bluetooth.DeviceEvent = function() {}; -/** @param {function(!chrome.bluetooth.Device): void} callback */ +/** + * @param {function(!chrome.bluetooth.Device): void} callback + * @return {undefined} + */ chrome.bluetooth.DeviceEvent.prototype.addListener = function(callback) {}; -/** @param {function(!chrome.bluetooth.Device): void} callback */ +/** + * @param {function(!chrome.bluetooth.Device): void} callback + * @return {undefined} + */ chrome.bluetooth.DeviceEvent.prototype.removeListener = function(callback) {}; @@ -1041,6 +1097,7 @@ * function(!{socketId: number})} propertiesOrCallback * @param {function(!{socketId: number})=} opt_callback * @see https://developer.chrome.com/apps/bluetoothSocket#method-create + * @return {undefined} */ chrome.bluetoothSocket.create = function(propertiesOrCallback, opt_callback) {}; @@ -1050,6 +1107,7 @@ * @param {!chrome.bluetoothSocket.SocketProperties} properties * @param {function()=} opt_callback * @see https://developer.chrome.com/apps/bluetoothSocket#method-update + * @return {undefined} */ chrome.bluetoothSocket.update = function(socketId, properties, opt_callback) {}; @@ -1059,6 +1117,7 @@ * @param {boolean} paused * @param {function()=} opt_callback * @see https://developer.chrome.com/apps/bluetoothSocket#method-setPaused + * @return {undefined} */ chrome.bluetoothSocket.setPaused = function(socketId, paused, opt_callback) {}; @@ -1069,6 +1128,7 @@ * @param {!chrome.bluetoothSocket.ListenOptions|function()} optionsOrCallback * @param {function()=} opt_callback * @see https://developer.chrome.com/apps/bluetoothSocket#method-listenUsingRfcomm + * @return {undefined} */ chrome.bluetoothSocket.listenUsingRfcomm = function(socketId, uuid, optionsOrCallback, opt_callback) {}; @@ -1080,6 +1140,7 @@ * @param {!chrome.bluetoothSocket.ListenOptions|function()} optionsOrCallback * @param {function()=} opt_callback * @see https://developer.chrome.com/apps/bluetoothSocket#method-listenUsingL2cap + * @return {undefined} */ chrome.bluetoothSocket.listenUsingL2cap = function(socketId, uuid, optionsOrCallback, opt_callback) {}; @@ -1091,6 +1152,7 @@ * @param {string} uuid * @param {function()} callback * @see https://developer.chrome.com/apps/bluetoothSocket#method-connect + * @return {undefined} */ chrome.bluetoothSocket.connect = function(socketId, address, uuid, callback) {}; @@ -1099,6 +1161,7 @@ * @param {number} socketId * @param {function()=} opt_callback * @see https://developer.chrome.com/apps/bluetoothSocket#method-disconnect + * @return {undefined} */ chrome.bluetoothSocket.disconnect = function(socketId, opt_callback) {}; @@ -1107,6 +1170,7 @@ * @param {number} socketId * @param {function()=} opt_callback * @see https://developer.chrome.com/apps/bluetoothSocket#method-close + * @return {undefined} */ chrome.bluetoothSocket.close = function(socketId, opt_callback) {}; @@ -1116,6 +1180,7 @@ * @param {!ArrayBuffer} data * @param {function(number)=} opt_callback * @see https://developer.chrome.com/apps/bluetoothSocket#method-send + * @return {undefined} */ chrome.bluetoothSocket.send = function(socketId, data, opt_callback) {}; @@ -1124,6 +1189,7 @@ * @param {number} socketId * @param {function(!chrome.bluetoothSocket.SocketInfo)} callback * @see https://developer.chrome.com/apps/bluetoothSocket#method-getInfo + * @return {undefined} */ chrome.bluetoothSocket.getInfo = function(socketId, callback) {}; @@ -1131,6 +1197,7 @@ /** * @param {function(!Array<!chrome.bluetoothSocket.SocketInfo>)} callback * @see https://developer.chrome.com/apps/bluetoothSocket#method-getSockets + * @return {undefined} */ chrome.bluetoothSocket.getSockets = function(callback) {}; @@ -1161,6 +1228,7 @@ /** * @param {function(!chrome.bluetoothSocket.AcceptEventData): void} callback + * @return {undefined} */ chrome.bluetoothSocket.AcceptEvent.prototype.addListener = function(callback) {}; @@ -1168,6 +1236,7 @@ /** * @param {function(!chrome.bluetoothSocket.AcceptEventData): void} callback + * @return {undefined} */ chrome.bluetoothSocket.AcceptEvent.prototype.removeListener = function(callback) {}; @@ -1220,6 +1289,7 @@ /** * @param {function( * !chrome.bluetoothSocket.AcceptErrorEventData): void} callback + * @return {undefined} */ chrome.bluetoothSocket.AcceptErrorEvent.prototype.addListener = function(callback) {}; @@ -1228,6 +1298,7 @@ /** * @param {function( * !chrome.bluetoothSocket.AcceptErrorEventData): void} callback + * @return {undefined} */ chrome.bluetoothSocket.AcceptErrorEvent.prototype.removeListener = function(callback) {}; @@ -1277,6 +1348,7 @@ /** * @param {function(!chrome.bluetoothSocket.ReceiveEventData): void} callback + * @return {undefined} */ chrome.bluetoothSocket.ReceiveEvent.prototype.addListener = function(callback) {}; @@ -1284,6 +1356,7 @@ /** * @param {function(!chrome.bluetoothSocket.ReceiveEventData): void} callback + * @return {undefined} */ chrome.bluetoothSocket.ReceiveEvent.prototype.removeListener = function(callback) {}; @@ -1336,6 +1409,7 @@ /** * @param {function( * !chrome.bluetoothSocket.ReceiveErrorEventData): void} callback + * @return {undefined} */ chrome.bluetoothSocket.ReceiveErrorEvent.prototype.addListener = function(callback) {}; @@ -1344,6 +1418,7 @@ /** * @param {function( * !chrome.bluetoothSocket.ReceiveErrorEventData): void} callback + * @return {undefined} */ chrome.bluetoothSocket.ReceiveErrorEvent.prototype.removeListener = function(callback) {}; @@ -1460,6 +1535,7 @@ * propertiesOrCallback * @param {function()=} opt_callback * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-connect + * @return {undefined} */ chrome.bluetoothLowEnergy.connect = function(deviceAddress, propertiesOrCallback, opt_callback) {}; @@ -1468,6 +1544,7 @@ * @param {string} deviceAddress * @param {function()=} opt_callback * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-disconnect + * @return {undefined} */ chrome.bluetoothLowEnergy.disconnect = function(deviceAddress, opt_callback) {}; @@ -1476,6 +1553,7 @@ * @param {string} serviceId * @param {function(!chrome.bluetoothLowEnergy.Service)} callback * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-getService + * @return {undefined} */ chrome.bluetoothLowEnergy.getService = function(serviceId, callback) {}; @@ -1484,6 +1562,7 @@ * @param {string} deviceAddress * @param {function(!Array<!chrome.bluetoothLowEnergy.Service>)} callback * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-getServices + * @return {undefined} */ chrome.bluetoothLowEnergy.getServices = function(deviceAddress, callback) {}; @@ -1492,6 +1571,7 @@ * @param {string} characteristicId * @param {function(!chrome.bluetoothLowEnergy.Characteristic)} callback * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-getCharacteristic + * @return {undefined} */ chrome.bluetoothLowEnergy.getCharacteristic = function(characteristicId, callback) {}; @@ -1502,6 +1582,7 @@ * @param {function(!Array<!chrome.bluetoothLowEnergy.Characteristic>)} * callback * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-getCharacteristics + * @return {undefined} */ chrome.bluetoothLowEnergy.getCharacteristics = function(serviceId, callback) {}; @@ -1511,6 +1592,7 @@ * @param {string} serviceId * @param {function(!Array<!chrome.bluetoothLowEnergy.Service>)} callback * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-getIncludedServices + * @return {undefined} */ chrome.bluetoothLowEnergy.getIncludedServices = function(serviceId, callback) {}; @@ -1520,6 +1602,7 @@ * @param {string} descriptorId * @param {function(!chrome.bluetoothLowEnergy.Descriptor)} callback * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-getDescriptor + * @return {undefined} */ chrome.bluetoothLowEnergy.getDescriptor = function(descriptorId, callback) {}; @@ -1528,6 +1611,7 @@ * @param {string} characteristicId * @param {function(!Array<!chrome.bluetoothLowEnergy.Descriptor>)} callback * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-getDescriptors + * @return {undefined} */ chrome.bluetoothLowEnergy.getDescriptors = function(characteristicId, callback) {}; @@ -1537,6 +1621,7 @@ * @param {string} characteristicId * @param {function(!chrome.bluetoothLowEnergy.Characteristic)} callback * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-readCharacteristicValue + * @return {undefined} */ chrome.bluetoothLowEnergy.readCharacteristicValue = function(characteristicId, callback) {}; @@ -1547,6 +1632,7 @@ * @param {!ArrayBuffer} value * @param {function()} callback * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-writeCharacteristicValue + * @return {undefined} */ chrome.bluetoothLowEnergy.writeCharacteristicValue = function(characteristicId, value, callback) {}; @@ -1565,7 +1651,8 @@ * propertiesOrCallback * @param {function()=} opt_callback * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-startCharacteristicNotifications - */ + * @return {undefined} + */ chrome.bluetoothLowEnergy.startCharacteristicNotifications = function(characteristicId, propertiesOrCallback, opt_callback) {}; @@ -1574,7 +1661,8 @@ * @param {string} characteristicId * @param {function()=} opt_callback * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-stopCharacteristicNotifications - */ + * @return {undefined} + */ chrome.bluetoothLowEnergy.stopCharacteristicNotifications = function(characteristicId, opt_callback) {}; @@ -1583,6 +1671,7 @@ * @param {string} descriptorId * @param {function(!chrome.bluetoothLowEnergy.Descriptor)} callback * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-readDescriptorValue + * @return {undefined} */ chrome.bluetoothLowEnergy.readDescriptorValue = function(descriptorId, callback) {}; @@ -1593,6 +1682,7 @@ * @param {!ArrayBuffer} value * @param {function()} callback * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-writeDescriptorValue + * @return {undefined} */ chrome.bluetoothLowEnergy.writeDescriptorValue = function(descriptorId, value, callback) {}; @@ -1605,12 +1695,18 @@ chrome.bluetoothLowEnergy.ServiceEvent = function() {}; -/** @param {function(!chrome.bluetoothLowEnergy.Service): void} callback */ +/** + * @param {function(!chrome.bluetoothLowEnergy.Service): void} callback + * @return {undefined} + */ chrome.bluetoothLowEnergy.ServiceEvent.prototype.addListener = function(callback) {}; -/** @param {function(!chrome.bluetoothLowEnergy.Service): void} callback */ +/** + * @param {function(!chrome.bluetoothLowEnergy.Service): void} callback + * @return {undefined} + */ chrome.bluetoothLowEnergy.ServiceEvent.prototype.removeListener = function(callback) {}; @@ -1657,6 +1753,7 @@ /** * @param {function(!chrome.bluetoothLowEnergy.Characteristic): void} * callback + * @return {undefined} */ chrome.bluetoothLowEnergy.CharacteristicEvent.prototype.addListener = function(callback) {}; @@ -1665,6 +1762,7 @@ /** * @param {function(!chrome.bluetoothLowEnergy.Characteristic): void} * callback + * @return {undefined} */ chrome.bluetoothLowEnergy.CharacteristicEvent.prototype.removeListener = function(callback) {}; @@ -1701,6 +1799,7 @@ /** * @param {function(!chrome.bluetoothLowEnergy.Descriptor): void} * callback + * @return {undefined} */ chrome.bluetoothLowEnergy.DescriptorEvent.prototype.addListener = function(callback) {}; @@ -1709,6 +1808,7 @@ /** * @param {function(!chrome.bluetoothLowEnergy.Descriptor): void} * callback + * @return {undefined} */ chrome.bluetoothLowEnergy.DescriptorEvent.prototype.removeListener = function(callback) {}; @@ -1743,6 +1843,7 @@ /** * @param {function(Array<string>): void} callback Callback function. + * @return {undefined} */ chrome.commands.getAll = function(callback) {}; @@ -1852,6 +1953,7 @@ * @param {!Array<!chrome.copresence.Operation>} operations * @param {function(string): void} callback * @see https://developer.chrome.com/apps/copresence#method-execute + * @return {undefined} */ chrome.copresence.execute = function(operations, callback) {}; @@ -1868,6 +1970,7 @@ /** * @param {function(string, !Array<!chrome.copresence.Message>): void} callback + * @return {undefined} */ chrome.copresence.MessagesReceivedEvent.prototype.addListener = function(callback) {}; @@ -1875,6 +1978,7 @@ /** * @param {function(string, !Array<!chrome.copresence.Message>): void} callback + * @return {undefined} */ chrome.copresence.MessagesReceivedEvent.prototype.removeListener = function(callback) {}; @@ -1948,6 +2052,7 @@ * Web API. * @param {function(!ArrayBuffer): void=} callback Called back with the * challenge response. + * @return {undefined} */ chrome.enterprise.platformKeys.challengeMachineKey = function(challenge, callback) {}; @@ -1963,6 +2068,7 @@ * function will then generate a new Enterprise User Key. * @param {function(!ArrayBuffer): void=} callback Called back with the * challenge response. + * @return {undefined} */ chrome.enterprise.platformKeys.challengeUserKey = function(challenge, registerKey, callback) {}; @@ -1971,6 +2077,7 @@ /** * @param {function(!Array<!chrome.enterprise.Token>): void} callback Called * with an array of Tokens. + * @return {undefined} */ chrome.enterprise.platformKeys.getTokens = function(callback) {}; @@ -1979,6 +2086,7 @@ * @param {string} tokenId Id of cetificate token either "user" or "system". * @param {(function(!Array<!ArrayBuffer>): void)} callback Array of DER * encoded x.509 certificates. + * @return {undefined} */ chrome.enterprise.platformKeys.getCertificates = function(tokenId, callback) {}; @@ -1988,6 +2096,7 @@ * @param {!ArrayBuffer} certificate The DER encoding of a X.509 certificate. * @param {function(): void=} opt_callback Called back when this operation is * finished. + * @return {undefined} */ chrome.enterprise.platformKeys.importCertificate = function(tokenId, certificate, opt_callback) {}; @@ -1998,6 +2107,7 @@ * @param {!ArrayBuffer} certificate The DER encoding of a X.509 certificate. * @param {(function(): void)=} opt_callback Called back when this operation is * finished. + * @return {undefined} */ chrome.enterprise.platformKeys.removeCertificate = function(tokenId, certificate, opt_callback) {}; @@ -2010,6 +2120,16 @@ chrome.extension = {}; +/** + * @enum {string} + * @see https://developer.chrome.com/extensions/extension#type-ViewType + */ +chrome.extension.ViewType = { + TAB: '', + POPUP: '', +}; + + /** @type {!Object|undefined} */ chrome.extension.lastError = {}; @@ -2053,8 +2173,16 @@ /** - * @param {Object=} opt_fetchProperties An object with optional 'type' and - * optional 'windowId' keys. + * @typedef {?{ + * type: (!chrome.extension.ViewType|string|undefined), + * windowId: (number|undefined), + * tabId: (number|undefined) + * }} + */ +chrome.extension.ViewInfo; + +/** + * @param {?chrome.extension.ViewInfo=} opt_fetchProperties * @return {Array<Window>} The global JS objects for each content view. */ chrome.extension.getViews = function(opt_fetchProperties) {}; @@ -2062,12 +2190,14 @@ /** * @param {function(boolean): void} callback Callback function. + * @return {undefined} */ chrome.extension.isAllowedFileSchemeAccess = function(callback) {}; /** * @param {function(boolean): void} callback Callback function. + * @return {undefined} */ chrome.extension.isAllowedIncognitoAccess = function(callback) {}; @@ -2079,6 +2209,7 @@ * @param {*=} opt_request The request value, if arg1 was the extensionId. * @param {function(*): void=} opt_callback The callback function which * takes a JSON response object sent by the handler of the request. + * @return {undefined} */ chrome.extension.sendMessage = function( extensionIdOrRequest, opt_request, opt_callback) {}; @@ -2090,12 +2221,14 @@ * @param {*=} opt_request The request value, if arg1 was the extensionId. * @param {function(*): void=} opt_callback The callback function which * takes a JSON response object sent by the handler of the request. + * @return {undefined} */ chrome.extension.sendRequest = function(opt_arg1, opt_request, opt_callback) {}; /** * @param {string} data + * @return {undefined} */ chrome.extension.setUpdateUrlData = function(data) {}; @@ -2127,12 +2260,14 @@ /** * @param {function(!Window=): void} callback Callback function. + * @return {undefined} */ chrome.runtime.getBackgroundPage = function(callback) {}; /** * @param {function(): void=} opt_callback Callback function. + * @return {undefined} */ chrome.runtime.openOptionsPage = function(opt_callback) {}; @@ -2207,12 +2342,14 @@ /** * @param {string} url This may be used to clean up server-side data, do * analytics, and implement surveys. Maximum 255 characters. + * @return {undefined} */ chrome.runtime.setUninstallURL = function(url) {}; /** * Reloads the app or extension. + * @return {undefined} */ chrome.runtime.reload = function() {}; @@ -2221,6 +2358,7 @@ * @param {function(string, !Object=): void} callback Called with "throttled", * "no_update", or "update_available". If an update is available, the object * contains more information about the available update. + * @return {undefined} */ chrome.runtime.requestUpdateCheck = function(callback) {}; @@ -2228,6 +2366,7 @@ /** * Restart the ChromeOS device when the app runs in kiosk mode. Otherwise, it's * no-op. + * @return {undefined} */ chrome.runtime.restart = function() {}; @@ -2252,6 +2391,7 @@ * the native messaging host. If an error occurs while connecting to the * native messaging host, the callback will be called with no arguments and * chrome.runtime.lastError will be set to the error message. + * @return {undefined} */ chrome.runtime.sendNativeMessage = function( application, message, opt_callback) {}; @@ -2260,12 +2400,14 @@ /** * * @param {function(!Object)} callback + * @return {undefined} */ chrome.runtime.getPlatformInfo = function(callback) {}; /** * @param {function(!DirectoryEntry)} callback + * @return {undefined} */ chrome.runtime.getPackageDirectoryEntry = function(callback) {}; @@ -2320,12 +2462,14 @@ /** * @param {function(!Port): void} callback Callback. + * @return {undefined} */ chrome.runtime.PortEvent.prototype.addListener = function(callback) {}; /** * @param {function(!Port): void} callback Callback. + * @return {undefined} */ chrome.runtime.PortEvent.prototype.removeListener = function(callback) {}; @@ -2355,6 +2499,7 @@ /** * @param {function(*, !MessageSender, function(*): void): (boolean|undefined)} * callback Callback. + * @return {undefined} */ chrome.runtime.MessageSenderEvent.prototype.addListener = function(callback) {}; @@ -2362,6 +2507,7 @@ /** * @param {function(*, !MessageSender, function(*): void): (boolean|undefined)} * callback Callback. + * @return {undefined} */ chrome.runtime.MessageSenderEvent.prototype.removeListener = function(callback) {}; @@ -2389,6 +2535,16 @@ /** + * @enum {string} + * @see https://developer.chrome.com/extensions/tabs#type-TabStatus + */ +chrome.tabs.TabStatus = { + COMPLETE: '', + LOADING: '', +}; + + +/** * @typedef {?{ * code: (string|undefined), * file: (string|undefined), @@ -2417,6 +2573,7 @@ * @param {function(string):void=} opt_callback A callback function which * accepts the data URL string of a JPEG encoding of the visible area of the * captured tab. + * @return {undefined} */ chrome.tabs.captureVisibleTab = function(windowIdOrOptionsOrCallback, opt_optionsOrCallback, opt_callback) {}; @@ -2425,6 +2582,7 @@ /** * @param {number} tabId Tab Id. * @param {{name: (string|undefined)}=} connectInfo Info Object. + * @return {Port} New port. */ chrome.tabs.connect = function(tabId, connectInfo) {}; @@ -2445,6 +2603,7 @@ /** * @param {!chrome.tabs.CreateProperties} createProperties Info object. * @param {function(!Tab): void=} opt_callback The callback function. + * @return {undefined} */ chrome.tabs.create = function(createProperties, opt_callback) {}; @@ -2457,6 +2616,7 @@ * @param {function(string): void=} opt_callback An optional callback function * that will be invoked with the language of the tab specified as first * argument. + * @return {undefined} */ chrome.tabs.detectLanguage = function(tabIdOrCallback, opt_callback) {}; @@ -2475,6 +2635,7 @@ * @param {function(!Array<*>):void=} opt_callback A callback that will be * invoked with the result of the execution of the script in every * injected frame. + * @return {undefined} */ chrome.tabs.executeScript = function(tabIdOrDetails, opt_detailsOrCallback, opt_callback) {}; @@ -2483,6 +2644,7 @@ /** * @param {number} tabId Tab id. * @param {function(!Tab): void} callback Callback. + * @return {undefined} */ chrome.tabs.get = function(tabId, callback) {}; @@ -2495,12 +2657,14 @@ * @param {number?} windowId Window id. * @param {function(Array<Tab>): void} callback Callback. * @deprecated Please use tabs.query {windowId: windowId}. + * @return {undefined} */ chrome.tabs.getAllInWindow = function(windowId, callback) {}; /** * @param {function(!Tab=): void} callback Callback. + * @return {undefined} */ chrome.tabs.getCurrent = function(callback) {}; @@ -2513,6 +2677,7 @@ * @param {number?} windowId Window id. * @param {function(Tab): void} callback Callback. * @deprecated Please use tabs.query({active: true}). + * @return {undefined} */ chrome.tabs.getSelected = function(windowId, callback) {}; @@ -2530,6 +2695,7 @@ * @param {!chrome.tabs.HighlightInfo} highlightInfo * @param {function(!Window): void} callback Callback function invoked * with each appropriate Window. + * @return {undefined} */ chrome.tabs.highlight = function(highlightInfo, callback) {}; @@ -2546,6 +2712,7 @@ * callback that will be invoked after the CSS has been injected. * @param {function():void=} opt_callback A callback that will be invoked after * the CSS has been injected. + * @return {undefined} */ chrome.tabs.insertCSS = function(tabIdOrDetails, opt_detailsOrCallback, opt_callback) {}; @@ -2564,6 +2731,7 @@ * @param {number|!Array<number>} tabId Tab id or array of tab ids. * @param {!chrome.tabs.MoveProperties} moveProperties * @param {function((!Tab|!Array<!Tab>)): void=} opt_callback Callback. + * @return {undefined} */ chrome.tabs.move = function(tabId, moveProperties, opt_callback) {}; @@ -2575,7 +2743,7 @@ * highlighted: (boolean|undefined), * currentWindow: (boolean|undefined), * lastFocusedWindow: (boolean|undefined), - * status: (string|undefined), + * status: (!chrome.tabs.TabStatus|string|undefined), * title: (string|undefined), * url: (!Array<string>|string|undefined), * windowId: (number|undefined), @@ -2589,6 +2757,7 @@ /** * @param {!chrome.tabs.QueryInfo} queryInfo * @param {function(!Array<!Tab>): void} callback Callback. + * @return {undefined} */ chrome.tabs.query = function(queryInfo, callback) {}; @@ -2598,6 +2767,7 @@ * @param {number} tabId The ID of the tab which is to be duplicated. * @param {(function(!Tab=):void)=} opt_callback A callback to be invoked with * details about the duplicated tab. + * @return {undefined} */ chrome.tabs.duplicate = function(tabId, opt_callback) {}; @@ -2624,6 +2794,7 @@ * reload is complete, if no object needs to be specified. * @param {function():void=} opt_callback A callback to be invoked when the * reload is complete. + * @return {undefined} */ chrome.tabs.reload = function(opt_tabIdOrReloadPropertiesOrCallback, opt_reloadPropertiesOrCallback, opt_callback) {}; @@ -2632,6 +2803,7 @@ /** * @param {number|!Array<number>} tabIds A tab ID or an array of tab IDs. * @param {function(): void=} opt_callback Callback. + * @return {undefined} */ chrome.tabs.remove = function(tabIds, opt_callback) {}; @@ -2652,6 +2824,7 @@ * callback function. * @param {function(*): void=} opt_callback The callback function which * takes a JSON response object sent by the handler of the request. + * @return {undefined} */ chrome.tabs.sendMessage = function(tabId, request, opt_optionsOrCallback, opt_callback) {}; @@ -2663,6 +2836,7 @@ * @param {function(*): void=} opt_callback The callback function which * takes a JSON response object sent by the handler of the request. * @deprecated Please use runtime.sendMessage. + * @return {undefined} */ chrome.tabs.sendRequest = function(tabId, request, opt_callback) {}; @@ -2691,6 +2865,7 @@ * that will be invoked with information about the tab being updated. * @param {function(!Tab=): void=} opt_callback An optional callback that will * be invoked with information about the tab being updated. + * @return {undefined} */ chrome.tabs.update = function(tabIdOrUpdateProperties, opt_updatePropertiesOrCallback, opt_callback) {}; @@ -2786,6 +2961,7 @@ * @param {function(!Array<!chrome.topSites.MostVisitedURL>)} callback Invoked * with a list of most visited URLs. * @see https://developer.chrome.com/extensions/topSites#method-get + * @return {undefined} */ chrome.topSites.get = function(callback) {}; @@ -2801,6 +2977,7 @@ * @param {Object=} opt_createData May have many keys to specify parameters. * Or the callback. * @param {function(ChromeWindow): void=} opt_callback Callback. + * @return {undefined} */ chrome.windows.create = function(opt_createData, opt_callback) {}; @@ -2810,6 +2987,7 @@ * @param {Object=} opt_getInfo May have 'populate' key. Or the callback. * @param {function(!ChromeWindow): void=} opt_callback Callback when * opt_getInfo is an object. + * @return {undefined} */ chrome.windows.get = function(id, opt_getInfo, opt_callback) {}; @@ -2817,6 +2995,7 @@ /** * @param {Object=} opt_getInfo May have 'populate' key. Or the callback. * @param {function(!Array<!ChromeWindow>): void=} opt_callback Callback. + * @return {undefined} */ chrome.windows.getAll = function(opt_getInfo, opt_callback) {}; @@ -2824,6 +3003,7 @@ /** * @param {Object=} opt_getInfo May have 'populate' key. Or the callback. * @param {function(ChromeWindow): void=} opt_callback Callback. + * @return {undefined} */ chrome.windows.getCurrent = function(opt_getInfo, opt_callback) { }; @@ -2831,6 +3011,7 @@ /** * @param {Object=} opt_getInfo May have 'populate' key. Or the callback. * @param {function(ChromeWindow): void=} opt_callback Callback. + * @return {undefined} */ chrome.windows.getLastFocused = function(opt_getInfo, opt_callback) { }; @@ -2838,6 +3019,7 @@ /** * @param {number} tabId Tab Id. * @param {function(): void=} opt_callback Callback. + * @return {undefined} */ chrome.windows.remove = function(tabId, opt_callback) {}; @@ -2847,6 +3029,7 @@ * @param {Object} updateProperties An object which may have many keys for * various options. * @param {function(): void=} opt_callback Callback. + * @return {undefined} */ chrome.windows.update = function(tabId, updateProperties, opt_callback) {}; @@ -2888,6 +3071,7 @@ * @param {function(Array<string>): void} callback The callback function which * accepts an array of the accept languages of the browser, such as * 'en-US','en','zh-CN'. + * @return {undefined} */ chrome.i18n.getAcceptLanguages = function(callback) {}; @@ -2910,6 +3094,7 @@ * @param {string} text User input string to be detected. * @param {function(!Object)} callback The callback for passing back the * language detection result. + * @return {undefined} */ chrome.i18n.detectLanguage = function(text, callback) {}; @@ -2923,6 +3108,7 @@ /** * @param {number} tabId Tab Id. + * @return {undefined} */ chrome.pageAction.hide = function(tabId) {}; @@ -2930,24 +3116,28 @@ /** * @param {Object} details An object which has 'tabId' and either * 'imageData' or 'path'. + * @return {undefined} */ chrome.pageAction.setIcon = function(details) {}; /** * @param {Object} details An object which may have 'popup' or 'tabId' as keys. + * @return {undefined} */ chrome.pageAction.setPopup = function(details) {}; /** * @param {Object} details An object which has 'tabId' and 'title'. + * @return {undefined} */ chrome.pageAction.setTitle = function(details) {}; /** * @param {number} tabId Tab Id. + * @return {undefined} */ chrome.pageAction.show = function(tabId) {}; @@ -2968,6 +3158,7 @@ * @param {(function(): void)=} opt_callback The callback function. If an error * occurs opening the URL, chrome.runtime.lastError will be set to the error * message. + * @return {undefined} */ chrome.browser.openTab = function(details, opt_callback) {}; @@ -3010,6 +3201,7 @@ * tabId: (number|undefined) * }} details * @see https://developer.chrome.com/extensions/browserAction#method-setTitle + * @return {undefined} */ chrome.browserAction.setTitle = function(details) {}; @@ -3018,6 +3210,7 @@ * @param {!chrome.browserAction.Tab} details * @param {function(string): void} callback * @see https://developer.chrome.com/extensions/browserAction#method-getTitle + * @return {undefined} */ chrome.browserAction.getTitle = function(details, callback) {}; @@ -3026,6 +3219,7 @@ * @param {!chrome.browserAction.SetIconImageData} details * @param {function(): void=} opt_callback * @see https://developer.chrome.com/extensions/browserAction#method-setIcon + * @return {undefined} */ chrome.browserAction.setIcon = function(details, opt_callback) {}; @@ -3036,6 +3230,7 @@ * popup: string * }} details * @see https://developer.chrome.com/extensions/browserAction#method-setPopup + * @return {undefined} */ chrome.browserAction.setPopup = function(details) {}; @@ -3044,6 +3239,7 @@ * @param {!chrome.browserAction.Tab} details * @param {function(string): void} callback * @see https://developer.chrome.com/extensions/browserAction#method-getPopup + * @return {undefined} */ chrome.browserAction.getPopup = function(details, callback) {}; @@ -3054,6 +3250,7 @@ * tabId: (number|undefined) * }} details * @see https://developer.chrome.com/extensions/browserAction#method-setBadgeText + * @return {undefined} */ chrome.browserAction.setBadgeText = function(details) {}; @@ -3062,6 +3259,7 @@ * @param {!chrome.browserAction.Tab} details * @param {function(string): void} callback * @see https://developer.chrome.com/extensions/browserAction#method-getBadgeText + * @return {undefined} */ chrome.browserAction.getBadgeText = function(details, callback) {}; @@ -3072,6 +3270,7 @@ * tabId: (number|undefined) * }} details * @see https://developer.chrome.com/extensions/browserAction#method-setBadgeBackgroundColor + * @return {undefined} */ chrome.browserAction.setBadgeBackgroundColor = function(details) {}; @@ -3080,6 +3279,7 @@ * @param {!chrome.browserAction.Tab} details * @param {function(chrome.browserAction.ColorArray): void} callback * @see https://developer.chrome.com/extensions/browserAction#method-getBadgeBackgroundColor + * @return {undefined} */ chrome.browserAction.getBadgeBackgroundColor = function(details, callback) {}; @@ -3087,6 +3287,7 @@ /** * @param {number=} opt_tabId * @see https://developer.chrome.com/extensions/browserAction#method-enable + * @return {undefined} */ chrome.browserAction.enable = function(opt_tabId) {}; @@ -3094,6 +3295,7 @@ /** * @param {number=} opt_tabId * @see https://developer.chrome.com/extensions/browserAction#method-disable + * @return {undefined} */ chrome.browserAction.disable = function(opt_tabId) {}; @@ -3106,6 +3308,7 @@ /** * @param {function(!Tab): void} callback + * @return {undefined} */ chrome.browserAction.BrowserActionTabEvent.prototype.addListener = function(callback) {}; @@ -3113,6 +3316,7 @@ /** * @param {function(!Tab): void} callback + * @return {undefined} */ chrome.browserAction.BrowserActionTabEvent.prototype.removeListener = function(callback) {}; @@ -3224,6 +3428,7 @@ * @param {chrome.bookmarks.CreateDetails} bookmark * @param {function(BookmarkTreeNode): void=} opt_callback The * callback function which accepts a BookmarkTreeNode object. + * @return {undefined} */ chrome.bookmarks.create = function(bookmark, opt_callback) {}; @@ -3234,6 +3439,7 @@ * optional 'index'. * @param {function(BookmarkTreeNode): void=} opt_callback * The callback function which accepts a BookmarkTreeNode object. + * @return {undefined} */ chrome.bookmarks.move = function(id, destination, opt_callback) {}; @@ -3243,6 +3449,7 @@ * @param {Object} changes An object which may have 'title' as a key. * @param {function(BookmarkTreeNode): void=} opt_callback The * callback function which accepts a BookmarkTreeNode object. + * @return {undefined} */ chrome.bookmarks.update = function(id, changes, opt_callback) {}; @@ -3250,6 +3457,7 @@ /** * @param {string} id * @param {function(): void=} opt_callback + * @return {undefined} */ chrome.bookmarks.remove = function(id, opt_callback) {}; @@ -3257,18 +3465,21 @@ /** * @param {string} id * @param {function(): void=} opt_callback + * @return {undefined} */ chrome.bookmarks.removeTree = function(id, opt_callback) {}; /** * @param {function(): void=} opt_callback + * @return {undefined} */ chrome.bookmarks.import = function(opt_callback) {}; /** * @param {function(): void=} opt_callback + * @return {undefined} */ chrome.bookmarks.export = function(opt_callback) {}; @@ -3324,12 +3535,14 @@ /** * @param {function(string, function(!Array<!SuggestResult>)): void} callback + * @return {undefined} */ chrome.omnibox.InputChangedEvent.prototype.addListener = function(callback) {}; /** * @param {function(string, function(!Array<!SuggestResult>)): void} callback + * @return {undefined} */ chrome.omnibox.InputChangedEvent.prototype.removeListener = function(callback) {}; @@ -3351,11 +3564,17 @@ chrome.omnibox.InputEnteredEvent = function() {}; -/** @param {function(string, string): void} callback */ +/** + * @param {function(string, string): void} callback + * @return {undefined} + */ chrome.omnibox.InputEnteredEvent.prototype.addListener = function(callback) {}; -/** @param {function(string, string): void} callback */ +/** + * @param {function(string, string): void} callback + * @return {undefined} + */ chrome.omnibox.InputEnteredEvent.prototype.removeListener = function(callback) {}; @@ -3373,6 +3592,7 @@ /** * @param {{description: string}} suggestion A partial SuggestResult object. + * @return {undefined} */ chrome.omnibox.setDefaultSuggestion = function(suggestion) {}; @@ -3449,6 +3669,7 @@ * @param {!chrome.contextMenus.UpdateProperties} updateProperties * @param {function()=} opt_callback * @see https://developer.chrome.com/extensions/contextMenus#method-update + * @return {undefined} */ chrome.contextMenus.update = function(id, updateProperties, opt_callback) {}; @@ -3457,6 +3678,7 @@ * @param {(number|string)} menuItemId * @param {function()=} opt_callback * @see https://developer.chrome.com/extensions/contextMenus#method-remove + * @return {undefined} */ chrome.contextMenus.remove = function(menuItemId, opt_callback) {}; @@ -3464,6 +3686,7 @@ /** * @param {function()=} opt_callback * @see https://developer.chrome.com/extensions/contextMenus#method-removeAll + * @return {undefined} */ chrome.contextMenus.removeAll = function(opt_callback) {}; @@ -3477,12 +3700,14 @@ /** * @param {function(!Object, !Tab=)} callback + * @return {undefined} */ chrome.contextMenus.ClickedEvent.prototype.addListener = function(callback) {}; /** * @param {function(!Object, !Tab=)} callback + * @return {undefined} */ chrome.contextMenus.ClickedEvent.prototype.removeListener = function(callback) {}; @@ -3534,6 +3759,7 @@ /** * @param {!chrome.cookies.CookieIdentifier} details * @param {function(Cookie=): void} callback + * @return {undefined} */ chrome.cookies.get = function(details, callback) {}; @@ -3541,12 +3767,14 @@ /** * @param {Object} details * @param {function(Array<Cookie>): void} callback + * @return {undefined} */ chrome.cookies.getAll = function(details, callback) {}; /** * @param {function(Array<CookieStore>): void} callback + * @return {undefined} */ chrome.cookies.getAllCookieStores = function(callback) {}; @@ -3556,6 +3784,7 @@ * @param {function(chrome.cookies.CookieIdentifier): void=} opt_callback If * removal failed for any reason, the parameter will be "null", and * "chrome.runtime.lastError" will be set. + * @return {undefined} */ chrome.cookies.remove = function(details, opt_callback) {}; @@ -3581,6 +3810,7 @@ * @param {function(Cookie): void=} opt_callback If setting failed for any * reason, the parameter will be "null", and "chrome.runtime.lastError" will * be set. + * @return {undefined} */ chrome.cookies.set = function(details, opt_callback) {}; @@ -3625,6 +3855,7 @@ * @param {string} id * @param {function(!ExtensionInfo): void=} opt_callback Optional callback * function. + * @return {undefined} */ chrome.management.get = function(id, opt_callback) {}; @@ -3640,6 +3871,7 @@ /** * @param {string} id The id of an already installed extension. * @param {function(!Array<string>)=} opt_callback Optional callback function. + * @return {undefined} */ chrome.management.getPermissionWarningsById = function(id, opt_callback) {}; @@ -3647,6 +3879,7 @@ /** * @param {string} manifestStr Extension's manifest JSON string. * @param {function(!Array<string>)=} opt_callback Optional callback function. + * @return {undefined} */ chrome.management.getPermissionWarningsByManifest = function(manifestStr, opt_callback) {}; @@ -3655,6 +3888,7 @@ /** * @param {string} id The id of an already installed extension. * @param {function(): void=} opt_callback Optional callback function. + * @return {undefined} */ chrome.management.launchApp = function(id, opt_callback) {}; @@ -3663,6 +3897,7 @@ * @param {string} id The id of an already installed extension. * @param {boolean} enabled Whether this item should be enabled. * @param {function(): void=} opt_callback Optional callback function. + * @return {undefined} */ chrome.management.setEnabled = function(id, enabled, opt_callback) {}; @@ -3673,6 +3908,7 @@ * opt_optionsOrCallback An optional uninstall options object or an optional * callback function. * @param {function(): void=} opt_callback Optional callback function. + * @return {undefined} */ chrome.management.uninstall = function(id, opt_optionsOrCallback, opt_callback) {}; @@ -3683,6 +3919,7 @@ * opt_optionsOrCallback An optional uninstall options object or an optional * callback function. * @param {function(): void=} opt_callback An optional callback function. + * @return {undefined} */ chrome.management.uninstallSelf = function(opt_optionsOrCallback, opt_callback) {}; @@ -3691,6 +3928,7 @@ /** * @param {string} id The id of an already installed extension. * @param {function(): void=} opt_callback Optional callback function. + * @return {undefined} */ chrome.management.createAppShortcut = function(id, opt_callback) {}; @@ -3701,6 +3939,7 @@ * value is in ExtensionInfo.availableLaunchTypes because the available * launch types vary on different platforms and configurations. * @param {function(): void=} opt_callback Optional callback function. + * @return {undefined} */ chrome.management.setLaunchType = function(id, launchType, opt_callback) {}; @@ -3711,6 +3950,7 @@ * @param {string} title The title of the generated app. * @param {function(!ExtensionInfo): void=} opt_callback Optional callback * function. + * @return {undefined} */ chrome.management.generateAppForLink = function(url, title, opt_callback) {}; @@ -3742,6 +3982,7 @@ * @param {number} thresholdSeconds Threshold in seconds, used to determine * when a machine is in the idle state. * @param {function(string): void} callback Callback to handle the state. + * @return {undefined} */ chrome.idle.queryState = function(thresholdSeconds, callback) {}; @@ -3749,6 +3990,7 @@ /** * @param {number} intervalInSeconds Threshold, in seconds, used to determine * when the system is in an idle state. + * @return {undefined} */ chrome.idle.setDetectionInterval = function(intervalInSeconds) {}; @@ -3817,6 +4059,7 @@ * Gets an array of all available voices. * @param {function(Array<TtsVoice>)=} opt_callback An optional callback * function. + * @return {undefined} */ chrome.tts.getVoices = function(opt_callback) {}; @@ -3824,6 +4067,7 @@ /** * Checks if the engine is currently speaking. * @param {function(boolean)=} opt_callback The callback function. + * @return {undefined} */ chrome.tts.isSpeaking = function(opt_callback) {}; @@ -3836,12 +4080,14 @@ * 32,768 characters. * @param {Object=} opt_options The speech options. * @param {function()=} opt_callback Called right away, before speech finishes. + * @return {undefined} */ chrome.tts.speak = function(utterance, opt_options, opt_callback) {}; /** * Stops any current speech. + * @return {undefined} */ chrome.tts.stop = function() {}; @@ -3914,6 +4160,7 @@ * @param {!chrome.fileBrowserHandler.SelectFileSelectionParams} selectionParams * Parameters that will be used while selecting the file. * @param {function(!Object)} callback Function called upon completion. + * @return {undefined} */ chrome.fileBrowserHandler.selectFile = function(selectionParams, callback) {}; @@ -3927,6 +4174,7 @@ /** * @param {function(string, !FileHandlerExecuteEventDetails)} callback + * @return {undefined} */ chrome.fileBrowserHandler.ExecuteEvent.prototype.addListener = function( callback) {}; @@ -3934,6 +4182,7 @@ /** * @param {function(string, !FileHandlerExecuteEventDetails)} callback + * @return {undefined} */ chrome.fileBrowserHandler.ExecuteEvent.prototype.removeListener = function( callback) {}; @@ -3984,6 +4233,7 @@ * send messages to the application. * @param {function(string): void} callback Function called when * registration completes with registration ID as argument. + * @return {undefined} */ chrome.gcm.register = function(senderIds, callback) {}; @@ -3992,6 +4242,7 @@ * Unregisters the application from GCM. * @see https://developer.chrome.com/extensions/gcm#method-unregister * @param {function(): void} callback Called when unregistration is done. + * @return {undefined} */ chrome.gcm.unregister = function(callback) {}; @@ -4001,6 +4252,7 @@ * @see https://developer.chrome.com/extensions/gcm#method-send * @param {!chrome.gcm.Message} message Message to be sent. * @param {function(string): void} callback Called with message ID. + * @return {undefined} */ chrome.gcm.send = function(message, callback) {}; @@ -4051,12 +4303,14 @@ /** * @param {function(!Object): void} callback Callback. + * @return {undefined} */ chrome.gcm.OnMessageEvent.prototype.addListener = function(callback) {}; /** * @param {function(!Object): void} callback Callback. + * @return {undefined} */ chrome.gcm.OnMessageEvent.prototype.removeListener = function(callback) {}; @@ -4083,12 +4337,14 @@ /** * @param {function(!Object): void} callback Callback. + * @return {undefined} */ chrome.gcm.OnSendErrorEvent.prototype.addListener = function(callback) {}; /** * @param {function(!Object): void} callback Callback. + * @return {undefined} */ chrome.gcm.OnSendErrorEvent.prototype.removeListener = function(callback) {}; @@ -4115,12 +4371,14 @@ /** * @param {Object<string, string>} details Object with a 'url' key. + * @return {undefined} */ chrome.history.addUrl = function(details) {}; /** * @param {function(): void} callback Callback function. + * @return {undefined} */ chrome.history.deleteAll = function(callback) {}; @@ -4129,12 +4387,14 @@ * @param {Object<string, string>} range Object with 'startTime' * and 'endTime' keys. * @param {function(): void} callback Callback function. + * @return {undefined} */ chrome.history.deleteRange = function(range, callback) {}; /** * @param {Object<string, string>} details Object with a 'url' key. + * @return {undefined} */ chrome.history.deleteUrl = function(details) {}; @@ -4178,6 +4438,7 @@ * specified. * @param {function(string=): void=} opt_callback A callback function if options * are specified. + * @return {undefined} */ chrome.identity.getAuthToken = function(detailsOrCallback, opt_callback) {}; @@ -4189,6 +4450,7 @@ /** * @param {chrome.identity.InvalidTokenDetails} details * @param {function(): void} callback + * @return {undefined} */ chrome.identity.removeCachedAuthToken = function(details, callback) {}; @@ -4200,6 +4462,7 @@ /** * @param {chrome.identity.WebAuthFlowDetails} details * @param {function(string=): void} callback + * @return {undefined} */ chrome.identity.launchWebAuthFlow = function(details, callback) {}; @@ -4208,7 +4471,10 @@ chrome.identity.WebAuthFlowDetails; -/** @param {function(!Object):void} callback */ +/** + * @param {function(!Object):void} callback + * @return {undefined} + */ chrome.identity.getProfileUserInfo = function(callback) {}; @@ -4239,6 +4505,7 @@ * @param {function(string, !ChromeKeyboardEvent): (boolean|undefined)} callback * callback. * @param {Array<string>=} opt_extraInfoSpec Array of extra information. + * @return {undefined} */ ChromeInputImeOnKeyEventEvent.prototype.addListener = function(callback, opt_extraInfoSpec) {}; @@ -4247,6 +4514,7 @@ /** * @param {function(string, !ChromeKeyboardEvent): (boolean|undefined)} callback * callback. + * @return {undefined} */ ChromeInputImeOnKeyEventEvent.prototype.removeListener = function(callback) {}; @@ -4254,6 +4522,7 @@ /** * @param {function(string, !ChromeKeyboardEvent): (boolean|undefined)} callback * callback. + * @return {undefined} */ ChromeInputImeOnKeyEventEvent.prototype.hasListener = function(callback) {}; @@ -4261,6 +4530,7 @@ /** * @param {function(string, !ChromeKeyboardEvent): (boolean|undefined)} callback * callback. + * @return {undefined} */ ChromeInputImeOnKeyEventEvent.prototype.hasListeners = function(callback) {}; @@ -4269,6 +4539,7 @@ * @param {!Object<string,number>} parameters An object with a * 'contextID' (number) key. * @param {function(boolean): void} callback Callback function. + * @return {undefined} */ chrome.input.ime.clearComposition = function(parameters, callback) {}; @@ -4277,6 +4548,7 @@ * @param {!Object<string,(string|number)>} parameters An object with * 'contextID' (number) and 'text' (string) keys. * @param {function(boolean): void=} opt_callback Callback function. + * @return {undefined} */ chrome.input.ime.commitText = function(parameters, opt_callback) {}; @@ -4285,6 +4557,7 @@ * @param {!Object<string,(string|number)>} parameters An object with * 'contextID' (number) and 'text' (string) keys. * @param {function(boolean): void=} opt_callback Callback function. + * @return {undefined} */ chrome.input.ime.deleteSurroundingText = function(parameters, opt_callback) {}; @@ -4294,6 +4567,7 @@ * parameters An object with 'engineID' (string) and 'properties' * (Object) keys. * @param {function(boolean): void=} opt_callback Callback function. + * @return {undefined} */ chrome.input.ime.setCandidateWindowProperties = function(parameters, opt_callback) {}; @@ -4304,6 +4578,7 @@ * parameters An object with 'contextID' (number) and 'candidates' * (array of object) keys. * @param {function(boolean): void=} opt_callback Callback function. + * @return {undefined} */ chrome.input.ime.setCandidates = function(parameters, opt_callback) {}; @@ -4314,6 +4589,7 @@ * 'selectionStart (number), 'selectionEnd' (number), 'cursor' (number), * and 'segments' (array of object) keys. * @param {function(boolean): void=} opt_callback Callback function. + * @return {undefined} */ chrome.input.ime.setComposition = function(parameters, opt_callback) {}; @@ -4322,6 +4598,7 @@ * @param {!Object<string,number>} parameters An object with * 'contextID' (number) and 'candidateID' (number) keys. * @param {function(boolean): void=} opt_callback Callback function. + * @return {undefined} */ chrome.input.ime.setCursorPosition = function(parameters, opt_callback) {}; @@ -4331,6 +4608,7 @@ * parameters An object with 'engineID' (string) and 'items' * (array of object) keys. * @param {function(): void=} opt_callback Callback function. + * @return {undefined} */ chrome.input.ime.setMenuItems = function(parameters, opt_callback) {}; @@ -4340,6 +4618,7 @@ * parameters An object with 'engineID' (string) and 'items' * (array of object) keys. * @param {function(): void=} opt_callback Callback function. + * @return {undefined} */ chrome.input.ime.updateMenuItems = function(parameters, opt_callback) {}; @@ -4348,6 +4627,7 @@ * @param {string} requestId Request id of the event that was handled. This * should come from keyEvent.requestId. * @param {boolean} response True if the keystroke was handled, false if not. + * @return {undefined} */ chrome.input.ime.keyEventHandled = function(requestId, response) {}; @@ -4406,6 +4686,7 @@ * interactive if permissions haven't been granted yet or the callback. * @param {function(!Array<!FileSystem>)=} opt_callback A success callback if * no details were supplied as arg1. + * @return {undefined} */ chrome.mediaGalleries.getMediaFileSystems = function( detailsOrCallback, opt_callback) {}; @@ -4413,6 +4694,7 @@ /** * @param {function(!Array<!FileSystem>, string)} callback Callback function. + * @return {undefined} */ chrome.mediaGalleries.addUserSelectedFolder = function(callback) {}; @@ -4420,6 +4702,7 @@ /** * @param {string} galleryId ID of the media gallery. * @param {function()=} opt_callback Optional callback function. + * @return {undefined} */ chrome.mediaGalleries.dropPermissionForMediaFileSystem = function(galleryId, opt_callback) {}; @@ -4433,6 +4716,7 @@ /** * @param {function(!Array<!FileSystem>)} callback Callback function. + * @return {undefined} */ chrome.mediaGalleries.addScanResults = function(callback) {}; @@ -4460,6 +4744,7 @@ /** * @param {function(!Array<!chrome.mediaGalleries.MediaFileSystemMetadata>)} * callback Callback function. + * @return {undefined} */ chrome.mediaGalleries.getAllMediaFileSystemMetadata = function(callback) {}; @@ -4521,6 +4806,7 @@ * 'all' if the metadataType is omitted. * @param {function(!chrome.mediaGalleries.MetaData)=} opt_callback If options * were passed as arg2, the callback to invoke with the metadata. + * @return {undefined} */ chrome.mediaGalleries.getMetadata = function( mediaFile, optionsOrCallback, opt_callback) {}; @@ -4536,12 +4822,14 @@ * @param {string} galleryId The media gallery's ID. * @param {!chrome.mediaGalleries.AddGalleryWatchCallback} callback Fired with * success or failure result. + * @return {undefined} */ chrome.mediaGalleries.addGalleryWatch = function(galleryId, callback) {}; /** * @param {string} galleryId The media gallery's ID. + * @return {undefined} */ chrome.mediaGalleries.removeGalleryWatch = function(galleryId) {}; @@ -4549,6 +4837,7 @@ /** * @param {function(!Array<string>): void} callback Callback function notifies * which galleries are being watched. + * @return {undefined} */ chrome.mediaGalleries.getAllGalleryWatch = function(callback) {}; @@ -4571,6 +4860,7 @@ /** * @param {!chrome.mediaGalleries.GalleryChangeCallback} callback + * @return {undefined} */ chrome.mediaGalleries.GalleryChangeEvent.prototype.addListener = function(callback) {}; @@ -4578,6 +4868,7 @@ /** * @param {!chrome.mediaGalleries.GalleryChangeCallback} callback + * @return {undefined} */ chrome.mediaGalleries.GalleryChangeEvent.prototype.removeListener = function(callback) {}; @@ -4585,6 +4876,7 @@ /** * @param {!chrome.mediaGalleries.GalleryChangeCallback} callback + * @return {undefined} */ chrome.mediaGalleries.GalleryChangeEvent.prototype.hasListener = function(callback) {}; @@ -4623,12 +4915,18 @@ chrome.mediaGalleries.ScanProgressEvent = function() {}; -/** @param {function(!chrome.mediaGalleries.OnScanProgressDetails)} callback */ +/** + * @param {function(!chrome.mediaGalleries.OnScanProgressDetails)} callback + * @return {undefined} + */ chrome.mediaGalleries.ScanProgressEvent.prototype.addListener = function(callback) {}; -/** @param {function(!chrome.mediaGalleries.OnScanProgressDetails)} callback */ +/** + * @param {function(!chrome.mediaGalleries.OnScanProgressDetails)} callback + * @return {undefined} + */ chrome.mediaGalleries.ScanProgressEvent.prototype.removeListener = function(callback) {}; @@ -4659,6 +4957,7 @@ /** * @param {Object<string, number>} details Object with a 'tabId' (number) key. * @param {function(Blob=): void} callback Callback function. + * @return {undefined} */ chrome.pageCapture.saveAsMHTML = function(details, callback) {}; @@ -4683,6 +4982,7 @@ /** * @param {!chrome.permissions.Permissions} permissions * @param {function(boolean): void} callback Callback function. + * @return {undefined} */ chrome.permissions.contains = function(permissions, callback) {}; @@ -4690,6 +4990,7 @@ /** * @param {function(!chrome.permissions.Permissions): void} callback * Callback function. + * @return {undefined} */ chrome.permissions.getAll = function(callback) {}; @@ -4697,6 +4998,7 @@ /** * @param {!chrome.permissions.Permissions} permissions * @param {function(boolean): void=} opt_callback Callback function. + * @return {undefined} */ chrome.permissions.remove = function(permissions, opt_callback) {}; @@ -4704,6 +5006,7 @@ /** * @param {!chrome.permissions.Permissions} permissions * @param {function(boolean): void=} opt_callback Callback function. + * @return {undefined} */ chrome.permissions.request = function(permissions, opt_callback) {}; @@ -4725,12 +5028,14 @@ /** * @param {string} level A string describing the degree to which power * management should be disabled, should be either "system" or "display". + * @return {undefined} */ chrome.power.requestKeepAwake = function(level) {}; /** * Releases a request previously made via requestKeepAwake(). + * @return {undefined} */ chrome.power.releaseKeepAwake = function() {}; @@ -4903,12 +5208,14 @@ * socket options or callback. * @param {function(!chrome.socket.CreateInfo)=} opt_callback Called when the * socket has been created. + * @return {undefined} */ chrome.socket.create = function(type, optionsOrCallback, opt_callback) {}; /** * @param {number} socketId The id of the socket to destroy. + * @return {undefined} */ chrome.socket.destroy = function(socketId) {}; @@ -4919,6 +5226,7 @@ * @param {number} port The port of the remote machine. * @param {function(number)} callback Called when the connection attempt is * complete. + * @return {undefined} */ chrome.socket.connect = function(socketId, hostname, port, callback) {}; @@ -4928,12 +5236,14 @@ * @param {string} address The address of the local machine. * @param {number} port The port of the local machine. * @param {function(number)} callback Called when the bind attempt is complete. + * @return {undefined} */ chrome.socket.bind = function(socketId, address, port, callback) {}; /** * @param {number} socketId The id of the socket to disconnect. + * @return {undefined} */ chrome.socket.disconnect = function(socketId) {}; @@ -4944,6 +5254,7 @@ * read buffer size or the callback. * @param {function(!chrome.socket.ReadInfo)=} opt_callback Called with data * that was available to be read without blocking. + * @return {undefined} */ chrome.socket.read = function(socketId, bufferSizeOrCallback, opt_callback) {}; @@ -4953,6 +5264,7 @@ * @param {!ArrayBuffer} data The data to write. * @param {function(!chrome.socket.WriteInfo)} callback Called when the write * operation completes without blocking or an error occurs. + * @return {undefined} */ chrome.socket.write = function(socketId, data, callback) {}; @@ -4963,6 +5275,7 @@ * The read buffer size or the callback. * @param {function(!chrome.socket.RecvFromInfo)=} opt_callback Called with data * that was available to be read without blocking. + * @return {undefined} */ chrome.socket.recvFrom = function(socketId, bufferSizeOrCallback, opt_callback) {}; @@ -4975,6 +5288,7 @@ * @param {number} port The port of the remote machine. * @param {function(!chrome.socket.WriteInfo)} callback Called when the write * operation completes without blocking or an error occurs. + * @return {undefined} */ chrome.socket.sendTo = function(socketId, data, address, port, callback) {}; @@ -4988,6 +5302,7 @@ * socket's listen queue or the callback. * @param {function(number)=} opt_callback Called when the listen operation * completes. + * @return {undefined} */ chrome.socket.listen = function(socketId, address, port, backlogOrCallback, opt_callback) {}; @@ -4997,6 +5312,7 @@ * @param {number} socketId The id of the socket to accept a connection on. * @param {function(!chrome.socket.AcceptInfo)} callback Called when a new * socket is accepted. + * @return {undefined} */ chrome.socket.accept = function(socketId, callback) {}; @@ -5009,6 +5325,7 @@ * is 0) or the callback * @param {function(boolean)=} opt_callback Called when the setKeepAlive attempt * is complete. + * @return {undefined} */ chrome.socket.setKeepAlive = function(socketId, enable, delayOrCallback, opt_callback) {}; @@ -5019,6 +5336,7 @@ * @param {boolean} noDelay If true, disables Nagle's algorithm. * @param {function(boolean)} callback Called when the setNoDelay attempt is * complete. + * @return {undefined} */ chrome.socket.setNoDelay = function(socketId, noDelay, callback) {}; @@ -5027,6 +5345,7 @@ * @param {number} socketId The id of the socket. * @param {function(!chrome.socket.SocketInfo)} callback Called when the state * is available. + * @return {undefined} */ chrome.socket.getInfo = function(socketId, callback) {}; @@ -5034,6 +5353,7 @@ /** * @param {function(!Array<!chrome.socket.NetworkAdapterInfo>)} callback Called * when local adapter information is available. + * @return {undefined} */ chrome.socket.getNetworkList = function(callback) {}; @@ -5043,6 +5363,7 @@ * @param {string} address The group address to join. Domain names are not * supported. * @param {function(number)} callback Called when the join operation is done. + * @return {undefined} */ chrome.socket.joinGroup = function(socketId, address, callback) {}; @@ -5052,6 +5373,7 @@ * @param {string} address The group address to leave. Domain names are not * supported. * @param {function(number)} callback Called when the leave operation is done. + * @return {undefined} */ chrome.socket.leaveGroup = function(socketId, address, callback) {}; @@ -5061,6 +5383,7 @@ * @param {number} ttl The time-to-live value. * @param {function(number)} callback Called when the configuration operation is * done. + * @return {undefined} */ chrome.socket.setMulticastTimeToLive = function(socketId, ttl, callback) {}; @@ -5070,6 +5393,7 @@ * @param {boolean} enabled True to enable loopback mode. * @param {function(number)} callback Called when the configuration operation is * done. + * @return {undefined} */ chrome.socket.setMulticastLoopbackMode = function(socketId, enabled, callback) {}; @@ -5079,6 +5403,7 @@ * @param {number} socketId The id of the socket. * @param {function(!Array<string>)} callback Called with an array of string * groups. + * @return {undefined} */ chrome.socket.getJoinedGroups = function(socketId, callback) {}; @@ -5179,6 +5504,7 @@ * function(!Object)} propertiesOrCallback * @param {function(!Object)=} opt_callback * @see https://developer.chrome.com/apps/sockets_tcp#method-create + * @return {undefined} */ chrome.sockets.tcp.create = function(propertiesOrCallback, opt_callback) {}; @@ -5188,6 +5514,7 @@ * @param {!chrome.sockets.tcp.SocketProperties} properties * @param {function()=} opt_callback * @see https://developer.chrome.com/apps/sockets_tcp#method-update + * @return {undefined} */ chrome.sockets.tcp.update = function(socketId, properties, opt_callback) {}; @@ -5197,6 +5524,7 @@ * @param {boolean} paused * @param {function()=} opt_callback * @see https://developer.chrome.com/apps/sockets_tcp#method-setPaused + * @return {undefined} */ chrome.sockets.tcp.setPaused = function(socketId, paused, opt_callback) {}; @@ -5207,6 +5535,7 @@ * @param {(number|function(number))} delayOrCallback * @param {function(number)=} opt_callback * @see https://developer.chrome.com/apps/sockets_tcp#method-setKeepAlive + * @return {undefined} */ chrome.sockets.tcp.setKeepAlive = function(socketId, enable, delayOrCallback, opt_callback) {}; @@ -5217,6 +5546,7 @@ * @param {boolean} noDelay * @param {function(number)} callback * @see https://developer.chrome.com/apps/sockets_tcp#method-setNoDelay + * @return {undefined} */ chrome.sockets.tcp.setNoDelay = function(socketId, noDelay, callback) {}; @@ -5227,6 +5557,7 @@ * @param {number} peerPort * @param {function(number)} callback * @see https://developer.chrome.com/apps/sockets_tcp#method-connect + * @return {undefined} */ chrome.sockets.tcp.connect = function(socketId, peerAddress, peerPort, callback) {}; @@ -5236,6 +5567,7 @@ * @param {number} socketId The id of the socket to disconnect. * @param {function()=} opt_callback * @see https://developer.chrome.com/apps/sockets_tcp#method-disconnect + * @return {undefined} */ chrome.sockets.tcp.disconnect = function(socketId, opt_callback) {}; @@ -5246,6 +5578,7 @@ * optionsOrCallback * @param {function(number)=} opt_callback * @see https://developer.chrome.com/apps/sockets_tcp#method-secure + * @return {undefined} */ chrome.sockets.tcp.secure = function(socketId, optionsOrCallback, opt_callback) {}; @@ -5256,6 +5589,7 @@ * @param {!ArrayBuffer} data * @param {function(!Object)} callback * @see https://developer.chrome.com/apps/sockets_tcp#method-send + * @return {undefined} */ chrome.sockets.tcp.send = function(socketId, data, callback) {}; @@ -5264,6 +5598,7 @@ * @param {number} socketId * @param {function()=} opt_callback * @see https://developer.chrome.com/apps/sockets_tcp#method-close + * @return {undefined} */ chrome.sockets.tcp.close = function(socketId, opt_callback) {}; @@ -5272,6 +5607,7 @@ * @param {number} socketId * @param {function(!chrome.sockets.tcp.SocketInfo)} callback * @see https://developer.chrome.com/apps/sockets_tcp#method-getInfo + * @return {undefined} */ chrome.sockets.tcp.getInfo = function(socketId, callback) {}; @@ -5279,6 +5615,7 @@ /** * @param {function(!Array<!chrome.sockets.tcp.SocketInfo>)} callback * @see https://developer.chrome.com/apps/sockets_tcp#method-getSockets + * @return {undefined} */ chrome.sockets.tcp.getSockets = function(callback) {}; @@ -5309,6 +5646,7 @@ /** * @param {function(!chrome.sockets.tcp.ReceiveEventData): void} callback + * @return {undefined} */ chrome.sockets.tcp.ReceiveEvent.prototype.addListener = function(callback) {}; @@ -5316,6 +5654,7 @@ /** * @param {function(!chrome.sockets.tcp.ReceiveEventData): void} callback + * @return {undefined} */ chrome.sockets.tcp.ReceiveEvent.prototype.removeListener = function(callback) {}; @@ -5364,6 +5703,7 @@ /** * @param {function( * !chrome.sockets.tcp.ReceiveErrorEventData): void} callback + * @return {undefined} */ chrome.sockets.tcp.ReceiveErrorEvent.prototype.addListener = function(callback) {}; @@ -5372,6 +5712,7 @@ /** * @param {function( * !chrome.sockets.tcp.ReceiveErrorEventData): void} callback + * @return {undefined} */ chrome.sockets.tcp.ReceiveErrorEvent.prototype.removeListener = function(callback) {}; @@ -5431,6 +5772,7 @@ /** * @param {function(!Object)} callback + * @return {undefined} */ chrome.system.cpu.getInfo = function(callback) {}; @@ -5563,6 +5905,7 @@ /** * @param {function(!Array<!chrome.system.display.DisplayInfo>)} * callback Called with an array of objects representing display info. + * @return {undefined} */ chrome.system.display.getInfo = function(callback) {}; @@ -5573,6 +5916,7 @@ * about display properties that should be changed. * @param {function()=} opt_callback The callback to execute when the display * info has been changed. + * @return {undefined} */ chrome.system.display.setDisplayProperties = function(id, info, opt_callback) {}; @@ -5600,6 +5944,7 @@ * @param {Object} details Object with a 'tabId' (number) key. * @param {function(!Array<Object<string, (boolean|number|string)>>)} callback * Callback function. + * @return {undefined} */ chrome.webNavigation.getAllFrames = function(details, callback) {}; @@ -5609,6 +5954,7 @@ * keys. * @param {function(Object<string, (boolean|string)>)} callback * Callback function. + * @return {undefined} */ chrome.webNavigation.getFrame = function(details, callback) {}; @@ -5665,6 +6011,7 @@ * the events that will be sent to this listener. * @param {Array<string>=} opt_extraInfoSpec Array of extra information * that should be passed to the listener function. + * @return {undefined} */ WebRequestEvent.prototype.addListener = function(listener, filter, opt_extraInfoSpec) {}; @@ -5673,6 +6020,7 @@ /** * @param {function(!Object): (void|!BlockingResponse)} listener Listener * function. + * @return {undefined} */ WebRequestEvent.prototype.removeListener = function(listener) {}; @@ -5680,6 +6028,7 @@ /** * @param {function(!Object): (void|!BlockingResponse)} listener Listener * function. + * @return {undefined} */ WebRequestEvent.prototype.hasListener = function(listener) {}; @@ -5687,6 +6036,7 @@ /** * @param {function(!Object): (void|!BlockingResponse)} listener Listener * function. + * @return {undefined} */ WebRequestEvent.prototype.hasListeners = function(listener) {}; @@ -5704,6 +6054,7 @@ * @param {function(!Object): void} listener Listener function. * @param {!RequestFilter} filter A set of filters that restrict * the events that will be sent to this listener. + * @return {undefined} */ WebRequestOnErrorOccurredEvent.prototype.addListener = function(listener, filter) {}; @@ -5711,18 +6062,21 @@ /** * @param {function(!Object): void} listener Listener function. + * @return {undefined} */ WebRequestOnErrorOccurredEvent.prototype.removeListener = function(listener) {}; /** * @param {function(!Object): void} listener Listener function. + * @return {undefined} */ WebRequestOnErrorOccurredEvent.prototype.hasListener = function(listener) {}; /** * @param {function(!Object): void} listener Listener function. + * @return {undefined} */ WebRequestOnErrorOccurredEvent.prototype.hasListeners = function(listener) {}; @@ -5736,6 +6090,7 @@ /** * @param {function(): void=} opt_callback Callback function. + * @return {undefined} */ chrome.webRequest.handlerBehaviorChanged = function(opt_callback) {}; @@ -5949,11 +6304,17 @@ function ChromeExtensionInfoEvent() {} -/** @param {function(!ExtensionInfo): void} callback */ +/** + * @param {function(!ExtensionInfo): void} callback + * @return {undefined} + */ ChromeExtensionInfoEvent.prototype.addListener = function(callback) {}; -/** @param {function(!ExtensionInfo): void} callback */ +/** + * @param {function(!ExtensionInfo): void} callback + * @return {undefined} + */ ChromeExtensionInfoEvent.prototype.removeListener = function(callback) {}; @@ -5989,6 +6350,7 @@ * not given, the callback. * @param {function(!chrome.pushMessaging.ChannelIdResult)=} opt_callback * Callback. + * @return {undefined} */ chrome.pushMessaging.getChannelId = function(interactiveOrCallback, opt_callback) {}; @@ -6004,6 +6366,7 @@ /** * @param {function(!chrome.pushMessaging.Message): void} callback + * @return {undefined} */ chrome.pushMessaging.PushMessageEvent.prototype.addListener = function(callback) {}; @@ -6011,6 +6374,7 @@ /** * @param {function(!chrome.pushMessaging.Message): void} callback + * @return {undefined} */ chrome.pushMessaging.PushMessageEvent.prototype.removeListener = function(callback) {}; @@ -6222,6 +6586,7 @@ /** * @param {!Object<string,string>} details Settings details. * @param {function(): void=} opt_callback Callback function. + * @return {undefined} */ ContentSetting.prototype.clear = function(details, opt_callback) {}; @@ -6230,12 +6595,14 @@ * @param {!Object<string,(string|boolean|ResourceIdentifier)>} details * Settings details. * @param {function(): void} callback Callback function. + * @return {undefined} */ ContentSetting.prototype.get = function(details, callback) {}; /** * @param {function(): void} callback Callback function. + * @return {undefined} */ ContentSetting.prototype.getResourceIdentifiers = function(callback) {}; @@ -6244,6 +6611,7 @@ * @param {!Object<string,(string|ResourceIdentifier)>} details * Settings details. * @param {function(): void=} opt_callback Callback function. + * @return {undefined} */ ContentSetting.prototype.set = function(details, opt_callback) {}; @@ -6496,19 +6864,29 @@ * Listener will receive an object that maps each key to its StorageChange, * and the namespace ("sync" or "local") of the storage area the changes * are for. + * @return {undefined} */ StorageChangeEvent.prototype.addListener = function(callback) {}; -/** @param {function(!Object<string, !StorageChange>, string)} callback */ +/** + * @param {function(!Object<string, !StorageChange>, string)} callback + * @return {undefined} + */ StorageChangeEvent.prototype.removeListener = function(callback) {}; -/** @param {function(!Object<string, !StorageChange>, string)} callback */ +/** + * @param {function(!Object<string, !StorageChange>, string)} callback + * @return {undefined} + */ StorageChangeEvent.prototype.hasListener = function(callback) {}; -/** @param {function(!Object<string, !StorageChange>, string)} callback */ +/** + * @param {function(!Object<string, !StorageChange>, string)} callback + * @return {undefined} + */ StorageChangeEvent.prototype.hasListeners = function(callback) {}; @@ -6545,6 +6923,7 @@ * result object. Pass in null to get the entire contents of storage. * @param {function(!Object)=} opt_callback Callback with storage items, or null * on failure. + * @return {undefined} */ StorageArea.prototype.get = function(keysOrCallback, opt_callback) {}; @@ -6555,6 +6934,7 @@ * will return 0. Pass in null to get the total usage of all of storage. * @param {function(number)=} opt_callback * Callback with the amount of space being used by storage. + * @return {undefined} */ StorageArea.prototype.getBytesInUse = function(keysOrCallback, opt_callback) {}; @@ -6564,6 +6944,7 @@ * Object specifying items to augment storage * with. Values that cannot be serialized (functions, etc) will be ignored. * @param {function()=} opt_callback Callback. + * @return {undefined} */ StorageArea.prototype.set = function(items, opt_callback) { }; @@ -6572,6 +6953,7 @@ * @param {(string|!Array<string>)} keys * A single key or a list of keys for items to remove. * @param {function()=} opt_callback Callback. + * @return {undefined} */ StorageArea.prototype.remove = function(keys, opt_callback) {}; @@ -6579,6 +6961,7 @@ /** * Removes all items from storage. * @param {function(): void=} opt_callback Callback function. + * @return {undefined} */ StorageArea.prototype.clear = function(opt_callback) {}; @@ -6594,6 +6977,7 @@ /** * @param {Object} details Object with a 'scope' (string) key. * @param {function(): void=} opt_callback Callback function. + * @return {undefined} */ ChromeSetting.prototype.clear = function(details, opt_callback) {}; @@ -6601,6 +6985,7 @@ /** * @param {Object} details Object with an 'incognito' (boolean) key. * @param {function(Object<string, *>): void} callback Callback function. + * @return {undefined} */ ChromeSetting.prototype.get = function(details, callback) {}; @@ -6609,6 +6994,7 @@ * @param {Object} details Object with a 'value' (*) key and an optional * 'scope' (string) key. * @param {function(): void=} opt_callback Callback function. + * @return {undefined} */ ChromeSetting.prototype.set = function(details, opt_callback) {}; @@ -6747,6 +7133,7 @@ * {@code chrome.fileSystem.restoreEntry}. * @param {function(string)} callback A success callback. * @see http://developer.chrome.com/apps/fileSystem.html#method-getDisplayPath + * @return {undefined} */ chrome.fileSystem.getDisplayPath = function(entry, callback) {}; @@ -6755,6 +7142,7 @@ * @param {!Entry} entry The entry to get a writable entry for. * @param {function(!Entry)} callback A success callback. * @see http://developer.chrome.com/apps/fileSystem.html#method-getWritableEntry + * @return {undefined} */ chrome.fileSystem.getWritableEntry = function(entry, callback) {}; @@ -6763,6 +7151,7 @@ * @param {!Entry} entry The entry to query writability. * @param {function(boolean)} callback A success callback. * @see http://developer.chrome.com/apps/fileSystem.html#method-isWritableEntry + * @return {undefined} */ chrome.fileSystem.isWritableEntry = function(entry, callback) {}; @@ -6823,6 +7212,7 @@ * @param {function(Entry=, !Array<!FileEntry>=)=} opt_callback A success * callback, if arg1 is options. * @see http://developer.chrome.com/apps/fileSystem.html#method-chooseEntry + * @return {undefined} */ chrome.fileSystem.chooseEntry = function(optionsOrCallback, opt_callback) {}; @@ -6831,6 +7221,7 @@ * @param {string} id The ID of the file entry to restore. * @param {function(!Entry)} callback A success callback. * @see http://developer.chrome.com/apps/fileSystem.html#method-restoreEntry + * @return {undefined} */ chrome.fileSystem.restoreEntry = function(id, callback) {}; @@ -6839,6 +7230,7 @@ * @param {string} id The ID of the file entry to query restorability. * @param {function(boolean)} callback A success callback. * @see http://developer.chrome.com/apps/fileSystem.html#method-isRestorable + * @return {undefined} */ chrome.fileSystem.isRestorable = function(id, callback) {}; @@ -6859,6 +7251,7 @@ * system in case of a success. Otherwise the error is passed as * chrome.runtime.lastError. * @see http://developer.chrome.com/apps/fileSystem.html#method-requestFileSystem + * @return {undefined} */ chrome.fileSystem.requestFileSystem = function(options, callback) {}; @@ -6868,6 +7261,7 @@ * callback with the file system list in case of a success. Otherwise the * error is passed as chrome.runtime.lastError. * @see http://developer.chrome.com/apps/fileSystem.html#method-getVolumeList + * @return {undefined} */ chrome.fileSystem.getVolumeList = function(callback) {}; @@ -6899,6 +7293,7 @@ * @param {function(!FileSystem)} callback A callback type for * requestFileSystem. * @see https://developer.chrome.com/apps/syncFileSystem#method-requestFileSystem + * @return {undefined} */ chrome.syncFileSystem.requestFileSystem = function(callback) {}; @@ -6915,6 +7310,7 @@ * @param {function()=} opt_callback * * @see https://developer.chrome.com/apps/syncFileSystem#method-setConflictResolutionPolicy + * @return {undefined} */ chrome.syncFileSystem.setConflictResolutionPolicy = function(policy, opt_callback) {}; @@ -6926,6 +7322,7 @@ * @param {function(string)} callback Accepting any of 'last_write_win' * or 'manual'. * @see https://developer.chrome.com/apps/syncFileSystem#method-getConflictResolutionPolicy + * @return {undefined} */ chrome.syncFileSystem.getConflictResolutionPolicy = function(callback) {}; @@ -6938,6 +7335,7 @@ * @param {function(!Object)} callback Taking an object substantially similar * to {@code {'usageBytes': number, quotaBytes: number}}. * @see https://developer.chrome.com/apps/syncFileSystem#method-getUsageAndQuota + * @return {undefined} */ chrome.syncFileSystem.getUsageAndQuota = function(fileSystem, callback) {}; @@ -6952,6 +7350,7 @@ * or 'conflicting'. * * @see https://developer.chrome.com/apps/syncFileSystem#method-getFileStatus + * @return {undefined} */ chrome.syncFileSystem.getFileStatus = function(fileEntry, callback) {}; @@ -6965,6 +7364,7 @@ * {@code {'fileEntry': Entry, 'status': string, 'error': string?}}. * * @see https://developer.chrome.com/apps/syncFileSystem#method-getFileStatuses + * @return {undefined} */ chrome.syncFileSystem.getFileStatuses = function(fileEntries, callback) {}; @@ -6978,6 +7378,7 @@ * 'authentication_required', 'temporary_unavailable', or 'disabled'. * * @see https://developer.chrome.com/apps/syncFileSystem#method-getServiceStatus + * @return {undefined} */ chrome.syncFileSystem.getServiceStatus = function(callback) {}; @@ -7021,6 +7422,7 @@ * @param {!chrome.alarms.AlarmCreateInfo=} opt_alarmInfo If a name was passed * as arg1, the info used to create the alarm. * @see http://developer.chrome.com/extensions/alarms.html#method-create + * @return {undefined} */ chrome.alarms.create = function(nameOrAlarmCreateInfo, opt_alarmInfo) {}; @@ -7033,6 +7435,7 @@ * @param {function(!chrome.alarms.Alarm)=} opt_callback If a name was passed * as arg1, the callback to invoke with the alarm. * @see http://developer.chrome.com/extensions/alarms.html#method-get + * @return {undefined} */ chrome.alarms.get = function(nameOrCallback, opt_callback) {}; @@ -7041,6 +7444,7 @@ * Gets an array of all the alarms. * @param {function(!Array<!chrome.alarms.Alarm>)} callback * @see http://developer.chrome.com/extensions/alarms.html#method-getAll + * @return {undefined} */ chrome.alarms.getAll = function(callback) {}; @@ -7051,6 +7455,7 @@ * @param {function(boolean)=} opt_callback A callback that will be called with * a boolean for whether the alarm was cleared. * @see http://developer.chrome.com/extensions/alarms.html#method-clear + * @return {undefined} */ chrome.alarms.clear = function(opt_name, opt_callback) {}; @@ -7060,6 +7465,7 @@ * @param {function(boolean)=} opt_callback A callback that will be called with * a boolean for whether the alarms were cleared. * @see http://developer.chrome.com/extensions/alarms.html#method-clearAll + * @return {undefined} */ chrome.alarms.clearAll = function(opt_callback) {}; @@ -7081,12 +7487,14 @@ /** * @param {function(!chrome.alarms.Alarm): void} callback + * @return {undefined} */ chrome.alarms.AlarmEvent.prototype.addListener = function(callback) {}; /** * @param {function(!chrome.alarms.Alarm): void} callback + * @return {undefined} */ chrome.alarms.AlarmEvent.prototype.removeListener = function(callback) {}; @@ -7322,6 +7730,7 @@ * for on target devices. * @param {function(!Array<!chrome.hid.HidDeviceInfo>)} callback Invoked with a * list of |HidDeviceInfo|s on complete. + * @return {undefined} */ chrome.hid.getDevices = function(options, callback) {}; @@ -7336,6 +7745,7 @@ * @param {!chrome.hid.HidGetUserSelectedDevicesOptions} options * @param {function(!Array<!chrome.hid.HidDeviceInfo>)} callback Invoked with a * list of |HidDeviceInfo|s on complete. + * @return {undefined} */ chrome.hid.getUserSelectedDevices = function(options, callback) {}; @@ -7346,6 +7756,7 @@ * @param {number} deviceId The ID of the device to open. * @param {function(!Object=)} callback Invoked with an |HidConnectInfo| if the * connection succeeds, or undefined if it fails. + * @return {undefined} */ chrome.hid.connect = function(deviceId, callback) {}; @@ -7356,6 +7767,7 @@ * @param {number} connectionId The connection to close. * @param {function()=} opt_callback The callback to invoke once the connection * is closed. + * @return {undefined} */ chrome.hid.disconnect = function(connectionId, opt_callback) {}; @@ -7366,6 +7778,7 @@ * @param {number} connectionId The connection from which to receive the report. * @param {function(number, !ArrayBuffer)} callback The callback to invoke with * the received report. + * @return {undefined} */ chrome.hid.receive = function(connectionId, callback) {}; @@ -7378,6 +7791,7 @@ * @param {!ArrayBuffer} data The report data. * @param {function()} callback The callback to invoke once the write is * finished. + * @return {undefined} */ chrome.hid.send = function(connectionId, reportId, data, callback) {}; @@ -7391,6 +7805,7 @@ * @param {number} size The size of the feature report to receive. * @param {function(!ArrayBuffer)} callback The callback to invoke with the * received report. + * @return {undefined} */ chrome.hid.receiveFeatureReport = function(connectionId, reportId, size, callback) {}; @@ -7405,6 +7820,7 @@ * @param {!ArrayBuffer} data The report data. * @param {function()} callback The callback to invoke once the write is * finished. + * @return {undefined} */ chrome.hid.sendFeatureReport = function(connectionId, reportId, data, callback) {}; @@ -7418,12 +7834,18 @@ chrome.hid.DeviceAddedEvent = function() {}; -/** @param {function(!chrome.hid.HidDeviceInfo): void} callback */ +/** + * @param {function(!chrome.hid.HidDeviceInfo): void} callback + * @return {undefined} + */ chrome.hid.DeviceAddedEvent.prototype.addListener = function(callback) {}; -/** @param {function(!chrome.hid.HidDeviceInfo): void} callback */ +/** + * @param {function(!chrome.hid.HidDeviceInfo): void} callback + * @return {undefined} + */ chrome.hid.DeviceAddedEvent.prototype.removeListener = function(callback) {}; @@ -7537,6 +7959,7 @@ * opt_optionsOrCallback * @param {(function(string): void)=} opt_callback * @see http://developer.chrome.com/extensions/notifications.html#method-create + * @return {undefined} */ chrome.notifications.create = function(notificationIdOrOptions, opt_optionsOrCallback, opt_callback) {}; @@ -7547,6 +7970,7 @@ * @param {!chrome.notifications.NotificationOptions} options * @param {chrome.notifications.BooleanCallback=} opt_callback * @see http://developer.chrome.com/extensions/notifications.html#method-update + * @return {undefined} */ chrome.notifications.update = function(notificationId, options, opt_callback) {}; @@ -7556,6 +7980,7 @@ * @param {string} notificationId * @param {!chrome.notifications.BooleanCallback=} opt_callback * @see http://developer.chrome.com/extensions/notifications.html#method-clear + * @return {undefined} */ chrome.notifications.clear = function(notificationId, opt_callback) {}; @@ -7563,6 +7988,7 @@ /** * @see http://developer.chrome.com/extensions/notifications.html#method-getAll * @param {!chrome.notifications.ObjectCallback} callback + * @return {undefined} */ chrome.notifications.getAll = function(callback) {}; @@ -7570,6 +7996,7 @@ /** * @see http://developer.chrome.com/extensions/notifications.html#method-getPermissionLevel * @param {function(string): void} callback takes 'granted' or 'denied' + * @return {undefined} */ chrome.notifications.getPermissionLevel = function(callback) {}; @@ -7623,12 +8050,14 @@ /** * @param {!chrome.notifications.ClosedCallback} callback + * @return {undefined} */ chrome.notifications.ClosedEvent.prototype.addListener = function(callback) {}; /** * @param {!chrome.notifications.ClosedCallback} callback + * @return {undefined} */ chrome.notifications.ClosedEvent.prototype.removeListener = function(callback) {}; @@ -7657,6 +8086,7 @@ /** * @param {!chrome.notifications.ButtonCallback} callback + * @return {undefined} */ chrome.notifications.ButtonClickedEvent.prototype.addListener = function(callback) {}; @@ -7664,6 +8094,7 @@ /** * @param {!chrome.notifications.ButtonCallback} callback + * @return {undefined} */ chrome.notifications.ButtonClickedEvent.prototype.removeListener = function(callback) {}; @@ -7719,12 +8150,18 @@ chrome.system.storage.StorageUnitInfoEvent = function() {}; -/** @param {function(!chrome.system.storage.StorageUnitInfo): void} callback */ +/** + * @param {function(!chrome.system.storage.StorageUnitInfo): void} callback + * @return {undefined} + */ chrome.system.storage.StorageUnitInfoEvent.prototype.addListener = function(callback) {}; -/** @param {function(!chrome.system.storage.StorageUnitInfo): void} callback */ +/** + * @param {function(!chrome.system.storage.StorageUnitInfo): void} callback + * @return {undefined} + */ chrome.system.storage.StorageUnitInfoEvent.prototype.removeListener = function(callback) {}; @@ -7753,6 +8190,7 @@ /** * Gets the storage information from the system. * @param {function(!Array<!chrome.system.storage.StorageUnitInfo>)} callback + * @return {undefined} */ chrome.system.storage.getInfo = function(callback) {}; @@ -7762,6 +8200,7 @@ * @param {string} id The transient device ID from StorageUnitInfo. * @param {function(string)} callback Callback function where the value * is any of: "success", "in_use", "no_such_device", "failure" + * @return {undefined} */ chrome.system.storage.ejectDevice = function(id, callback) {}; @@ -7771,6 +8210,7 @@ * @param {string} id The transient device ID from StorageUnitInfo. * @param {function(Object<string, number>)} callback A callback function that * accepts an object with {@code id} and {@code availableCapacity} fields. + * @return {undefined} */ chrome.system.storage.getAvailableCapacity = function(id, callback) {}; @@ -7885,7 +8325,7 @@ /** * @typedef {?{ - * deviceId: number, + * vendorId: number, * productId: number, * interfaceId: (number|undefined) * }} @@ -8050,6 +8490,7 @@ * search for on target devices. * @param {function(!Array<!chrome.usb.Device>)} callback Invoked with a list * of |Device|s on complete. + * @return {undefined} */ chrome.usb.getDevices = function(options, callback) {}; @@ -8060,6 +8501,7 @@ * the device picker dialog box. * @param {function(!Array<!chrome.usb.Device>)} callback Invoked with a list * of |Device|s on complete. + * @return {undefined} */ chrome.usb.getUserSelectedDevices = function(options, callback) {}; @@ -8069,6 +8511,7 @@ * @param {!chrome.usb.Device} device The device to fetch descriptors from. * @param {function(!Array<!chrome.usb.ConfigDescriptor>)} callback Invoked with * the full set of device configuration descriptors. + * @return {undefined} */ chrome.usb.getConfigurations = function(device, callback) {}; @@ -8078,6 +8521,7 @@ * @param {!chrome.usb.Device} device The device to request access to. * @param {number} interfaceId * @param {function(boolean)} callback + * @return {undefined} */ chrome.usb.requestAccess = function(device, interfaceId, callback) {}; @@ -8087,6 +8531,7 @@ * @param {!chrome.usb.Device} device The device to open. * @param {function(!chrome.usb.ConnectionHandle)} callback Invoked with the * created ConnectionHandle on complete. + * @return {undefined} */ chrome.usb.openDevice = function(device, callback) {}; @@ -8097,6 +8542,7 @@ * on target devices. * @param {function(!Array<!chrome.usb.ConnectionHandle>)} callback Invoked * with the opened ConnectionHandle on complete. + * @return {undefined} */ chrome.usb.findDevices = function(options, callback) {}; @@ -8106,6 +8552,7 @@ * @param {!chrome.usb.ConnectionHandle} handle The connection handle to close. * @param {function()=} opt_callback The callback to invoke once the device is * closed. + * @return {undefined} */ chrome.usb.closeDevice = function(handle, opt_callback) {}; @@ -8116,6 +8563,7 @@ * to select a device configuration. * @param {number} configurationValue The configuration to select. * @param {function()} callback The callback to invoke on complete. + * @return {undefined} */ chrome.usb.setConfiguration = function(handle, configurationValue, callback) {}; @@ -8126,6 +8574,7 @@ * to get the current device configuration descriptor. * @param {function(!chrome.usb.ConfigDescriptor)} callback The callback to * invoke on complete. + * @return {undefined} */ chrome.usb.getConfiguration = function(handle, callback) {}; @@ -8136,6 +8585,7 @@ * interfaces should be listed. * @param {function(!Array<!chrome.usb.InterfaceDescriptor>)} callback The * callback to invoke when the interfaces are enumerated. + * @return {undefined} */ chrome.usb.listInterfaces = function(handle, callback) {}; @@ -8147,6 +8597,7 @@ * @param {number} interfaceNumber * @param {function()} callback The callback to invoke once the interface is * claimed. + * @return {undefined} */ chrome.usb.claimInterface = function(handle, interfaceNumber, callback) {}; @@ -8158,6 +8609,7 @@ * @param {number} interfaceNumber * @param {function()} callback The callback to invoke once the interface is * released. + * @return {undefined} */ chrome.usb.releaseInterface = function(handle, interfaceNumber, callback) {}; @@ -8170,6 +8622,7 @@ * @param {number} alternateSetting The alternate setting to set. * @param {function()} callback The callback to invoke once the interface * setting is set. + * @return {undefined} */ chrome.usb.setInterfaceAlternateSetting = function( handle, interfaceNumber, alternateSetting, callback) {}; @@ -8183,6 +8636,7 @@ * transfer. * @param {function(!chrome.usb.TransferResultInfo)} callback Invoked once the * transfer has completed. + * @return {undefined} */ chrome.usb.controlTransfer = function(handle, transferInfo, callback) {}; @@ -8195,6 +8649,7 @@ * transfer. See GenericTransferInfo. * @param {function(!chrome.usb.TransferResultInfo)} callback Invoked once the * transfer has completed. + * @return {undefined} */ chrome.usb.bulkTransfer = function(handle, transferInfo, callback) {}; @@ -8207,6 +8662,7 @@ * transfer. See GenericTransferInfo. * @param {function(!chrome.usb.TransferResultInfo)} callback Invoked once the * transfer has completed. + * @return {undefined} */ chrome.usb.interruptTransfer = function(handle, transferInfo, callback) {}; @@ -8219,6 +8675,7 @@ * the transfer. * @param {function(!chrome.usb.TransferResultInfo)} callback Invoked once the * transfer has been completed. + * @return {undefined} */ chrome.usb.isochronousTransfer = function(handle, transferInfo, callback) {}; @@ -8228,6 +8685,7 @@ * @param {!chrome.usb.ConnectionHandle} handle A connection handle to reset. * @param {function(boolean)} callback Invoked once the device is reset with a * boolean indicating whether the reset completed successfully. + * @return {undefined} */ chrome.usb.resetDevice = function(handle, callback) {}; @@ -8240,11 +8698,17 @@ chrome.usb.DeviceEvent = function() {}; -/** @param {function(!chrome.usb.Device): void} callback */ +/** + * @param {function(!chrome.usb.Device): void} callback + * @return {undefined} + */ chrome.usb.DeviceEvent.prototype.addListener = function(callback) {}; -/** @param {function(!chrome.usb.Device): void} callback */ +/** + * @param {function(!chrome.usb.Device): void} callback + * @return {undefined} + */ chrome.usb.DeviceEvent.prototype.removeListener = function(callback) {}; @@ -8325,6 +8789,7 @@ * @param {function(!Array<!Object>)} callback Invoked with a * list of ports on complete. * @see https://developer.chrome.com/apps/serial#method-getDevices + * @return {undefined} */ chrome.serial.getDevices = function(callback) {}; @@ -8339,6 +8804,7 @@ * @param {function(!chrome.serial.ConnectionInfo)=} opt_callback Invoked with * the created ConnectionInfo on complete. * @see https://developer.chrome.com/apps/serial#method-connect + * @return {undefined} */ chrome.serial.connect = function(path, optionsOrCallback, opt_callback) {}; @@ -8351,6 +8817,7 @@ * @param {function(boolean)} callback Called when the configuration has * completed. * @see https://developer.chrome.com/apps/serial#method-update + * @return {undefined} */ chrome.serial.update = function(connectionId, options, callback) {}; @@ -8361,6 +8828,7 @@ * @param {function(boolean)} callback Called when the connection * has been closed. * @see https://developer.chrome.com/apps/serial#method-disconnect + * @return {undefined} */ chrome.serial.disconnect = function(connectionId, callback) {}; @@ -8371,6 +8839,7 @@ * @param {boolean} paused Flag to indicate whether to pause or unpause. * @param {function()} callback Called when the configuration has completed. * @see https://developer.chrome.com/apps/serial#method-setPaused + * @return {undefined} */ chrome.serial.setPaused = function(connectionId, paused, callback) {}; @@ -8381,6 +8850,7 @@ * @param {function(!chrome.serial.ConnectionInfo)} callback * Called with connection state information when available. * @see https://developer.chrome.com/apps/serial#method-getInfo + * @return {undefined} */ chrome.serial.getInfo = function(connectionId, callback) {}; @@ -8391,6 +8861,7 @@ * @param {function(!Array<!chrome.serial.ConnectionInfo>)} callback * Called with the list of |ConnectionInfo|s when available. * @see https://developer.chrome.com/apps/serial#method-getConnections + * @return {undefined} */ chrome.serial.getConnections = function(callback) {}; @@ -8402,6 +8873,7 @@ * @param {function(!Object)} callback Called when the operation has * completed. * @see https://developer.chrome.com/apps/serial#method-send + * @return {undefined} */ chrome.serial.send = function(connectionId, data, callback) {}; @@ -8411,6 +8883,7 @@ * @param {number} connectionId The id of the opened connection. * @param {function(boolean)} callback * @see https://developer.chrome.com/apps/serial#method-flush + * @return {undefined} */ chrome.serial.flush = function(connectionId, callback) {}; @@ -8422,6 +8895,7 @@ * @param {number} connectionId The id of the opened connection. * @param {function(!Object)} callback * @see https://developer.chrome.com/apps/serial#method-getControlSignals + * @return {undefined} */ chrome.serial.getControlSignals = function(connectionId, callback) {}; @@ -8443,6 +8917,7 @@ * @param {function(boolean)} callback Called once the control signals * have been set. * @see https://developer.chrome.com/apps/serial#method-setControlSignals + * @return {undefined} */ chrome.serial.setControlSignals = function(connectionId, signals, callback) {}; @@ -8477,12 +8952,14 @@ /** * @param {string} message Displayed on the unlock screen. + * @return {undefined} */ chrome.screenlockPrivate.showMessage = function(message) {}; /** * @param {function(boolean)} callback + * @return {undefined} */ chrome.screenlockPrivate.getLocked = function(callback) {}; @@ -8490,6 +8967,7 @@ /** * @param {boolean} locked If true and the screen is unlocked, locks the screen. * If false and the screen is locked, unlocks the screen. + * @return {undefined} */ chrome.screenlockPrivate.setLocked = function(locked) {}; @@ -8506,6 +8984,7 @@ /** * @param {function(string): void} callback + * @return {undefined} */ chrome.musicManagerPrivate.getDeviceId = function(callback) {}; @@ -8537,6 +9016,7 @@ /** * @param {string} galleryId * @param {!chrome.mediaGalleriesPrivate.AddGalleryWatchCallback} callback + * @return {undefined} */ chrome.mediaGalleriesPrivate.addGalleryWatch = function(galleryId, callback) {}; @@ -8572,6 +9052,7 @@ /** * @param {!chrome.mediaGalleriesPrivate.DeviceCallback} callback * @deprecated Use {chrome.system.storage.DeviceEvent.addListener}. + * @return {undefined} */ chrome.mediaGalleriesPrivate.DeviceEvent.prototype.addListener = function(callback) {}; @@ -8580,6 +9061,7 @@ /** * @param {!chrome.mediaGalleriesPrivate.DeviceCallback} callback * @deprecated Use {chrome.system.storage.DeviceEvent.removeListener}. + * @return {undefined} */ chrome.mediaGalleriesPrivate.DeviceEvent.prototype.removeListener = function(callback) {}; @@ -8588,6 +9070,7 @@ /** * @param {!chrome.mediaGalleriesPrivate.DeviceCallback} callback * @deprecated Use {chrome.system.storage.DeviceEvent.hasListener}. + * @return {undefined} */ chrome.mediaGalleriesPrivate.DeviceEvent.prototype.hasListener = function(callback) {}; @@ -8610,6 +9093,7 @@ /** * @param {!chrome.mediaGalleriesPrivate.GalleryChangeCallback} callback + * @return {undefined} */ chrome.mediaGalleriesPrivate.GalleryChangeEvent.prototype.addListener = function(callback) {}; @@ -8617,6 +9101,7 @@ /** * @param {!chrome.mediaGalleriesPrivate.GalleryChangeCallback} callback + * @return {undefined} */ chrome.mediaGalleriesPrivate.GalleryChangeEvent.prototype.removeListener = function(callback) {}; @@ -8624,6 +9109,7 @@ /** * @param {!chrome.mediaGalleriesPrivate.GalleryChangeCallback} callback + * @return {undefined} */ chrome.mediaGalleriesPrivate.GalleryChangeEvent.prototype.hasListener = function(callback) {}; @@ -8872,6 +9358,7 @@ /** * @param {string} guid * @param {function(!Object)} callback + * @return {undefined} */ chrome.networkingPrivate.getProperties = function(guid, callback) {}; @@ -8879,6 +9366,7 @@ /** * @param {string} guid * @param {function(!Object)} callback + * @return {undefined} */ chrome.networkingPrivate.getManagedProperties = function(guid, callback) {}; @@ -8886,6 +9374,7 @@ /** * @param {string} guid * @param {function(!chrome.networkingPrivate.NetworkStateProperties)} callback + * @return {undefined} */ chrome.networkingPrivate.getState = function(guid, callback) {}; @@ -8896,6 +9385,7 @@ * @param {string} guid * @param {!Object} properties * @param {function()=} opt_callback + * @return {undefined} */ chrome.networkingPrivate.setProperties = function(guid, properties, opt_callback) {}; @@ -8908,6 +9398,7 @@ * @param {!Object} properties * @param {function(string)=} opt_callback Returns guid of the configured * configuration. + * @return {undefined} */ chrome.networkingPrivate.createNetwork = function(shared, properties, opt_callback) {}; @@ -8916,6 +9407,7 @@ /** * @param {string} guid * @param {function()=} opt_callback Called when the operation has completed. + * @return {undefined} */ chrome.networkingPrivate.forgetNetwork = function(guid, opt_callback) {}; @@ -8924,6 +9416,7 @@ * @param {!chrome.networkingPrivate.NetworkFilter} filter * @param {function(!Array<!chrome.networkingPrivate.NetworkStateProperties>)} * callback + * @return {undefined} */ chrome.networkingPrivate.getNetworks = function(filter, callback) {}; @@ -8933,6 +9426,7 @@ * @param {function(!Array<!chrome.networkingPrivate.NetworkStateProperties>)} * callback * @deprecated Use chrome.networkingPrivate.getNetworks with filter.visible=true + * @return {undefined} */ chrome.networkingPrivate.getVisibleNetworks = function(type, callback) {}; @@ -8940,6 +9434,7 @@ /** * @param {function(!Array<string>)} callback * @deprecated Use chrome.networkingPrivate.getDeviceStates. + * @return {undefined} */ chrome.networkingPrivate.getEnabledNetworkTypes = function(callback) {}; @@ -8947,21 +9442,29 @@ /** * @param {function(!Array<!chrome.networkingPrivate.DeviceStateProperties>)} * callback + * @return {undefined} */ chrome.networkingPrivate.getDeviceStates = function(callback) {}; -/** @param {string} networkType */ +/** + * @param {string} networkType + * @return {undefined} + */ chrome.networkingPrivate.enableNetworkType = function(networkType) {}; -/** @param {string} networkType */ +/** + * @param {string} networkType + * @return {undefined} + */ chrome.networkingPrivate.disableNetworkType = function(networkType) {}; /** * Requests that the networking subsystem scan for new networks and update the * list returned by getVisibleNetworks. + * @return {undefined} */ chrome.networkingPrivate.requestNetworkScan = function() {}; @@ -8969,6 +9472,7 @@ /** * @param {string} guid * @param {function()=} opt_callback + * @return {undefined} */ chrome.networkingPrivate.startConnect = function(guid, opt_callback) {}; @@ -8976,6 +9480,7 @@ /** * @param {string} guid * @param {function()=} opt_callback + * @return {undefined} */ chrome.networkingPrivate.startDisconnect = function(guid, opt_callback) {}; @@ -8984,6 +9489,7 @@ * @param {string} guid * @param {(string|function())=} opt_carrierOrCallback * @param {function()=} opt_callback + * @return {undefined} */ chrome.networkingPrivate.startActivate = function(guid, opt_carrierOrCallback, opt_callback) {}; @@ -8992,6 +9498,7 @@ /** * @param {!chrome.networkingPrivate.VerificationProperties} verificationInfo * @param {function(boolean)} callback + * @return {undefined} */ chrome.networkingPrivate.verifyDestination = function(verificationInfo, callback) {}; @@ -9001,6 +9508,7 @@ * @param {!chrome.networkingPrivate.VerificationProperties} verificationInfo * @param {string} guid * @param {function(string)} callback + * @return {undefined} */ chrome.networkingPrivate.verifyAndEncryptCredentials = function(verificationInfo, guid, callback) {}; @@ -9010,6 +9518,7 @@ * @param {!chrome.networkingPrivate.VerificationProperties} verificationInfo * @param {string} data * @param {function(string)} callback + * @return {undefined} */ chrome.networkingPrivate.verifyAndEncryptData = function(verificationInfo, data, callback) {}; @@ -9019,6 +9528,7 @@ * @param {string} ipOrMacAddress * @param {boolean} enabled * @param {function(string)=} opt_callback + * @return {undefined} */ chrome.networkingPrivate.setWifiTDLSEnabledState = function(ipOrMacAddress, enabled, opt_callback) {}; @@ -9027,6 +9537,7 @@ /** * @param {string} ipOrMacAddress * @param {function(string)} callback + * @return {undefined} */ chrome.networkingPrivate.getWifiTDLSStatus = function(ipOrMacAddress, callback) {}; @@ -9035,6 +9546,7 @@ /** * @param {string} guid * @param {function(string)} callback + * @return {undefined} */ chrome.networkingPrivate.getCaptivePortalStatus = function(guid, callback) {}; @@ -9099,12 +9611,16 @@ /** * @param {function(!Array<!chrome.mdns.MdnsService>): void} callback * @param {!Object=} opt_filter + * @return {undefined} */ chrome.mdns.ServiceListEvent.prototype.addListener = function(callback, opt_filter) {}; -/** @param {function(!Array<!chrome.mdns.MdnsService>): void} callback */ +/** + * @param {function(!Array<!chrome.mdns.MdnsService>): void} callback + * @return {undefined} + */ chrome.mdns.ServiceListEvent.prototype.removeListener = function(callback) {}; @@ -9123,7 +9639,10 @@ chrome.mdns.onServiceList; -/** @param {function()} callback */ +/** + * @param {function()} callback + * @return {undefined} + */ chrome.mdns.forceDiscovery = function(callback) {}; @@ -9158,6 +9677,7 @@ * Returns the list of cloud devices visible locally or available in the * cloud for user account. * @param {function(!Array<!chrome.gcdPrivate.Device>): void} callback + * @return {undefined} */ chrome.gcdPrivate.getCloudDeviceList = function(callback) {}; @@ -9166,6 +9686,7 @@ * Queries network for local devices. Triggers onDeviceStateChanged and * onDeviceRemoved events. Call this function *only* after registering for * onDeviceStateChanged and onDeviceRemoved events, or it will do nothing. + * @return {undefined} */ chrome.gcdPrivate.queryForNewLocalDevices = function() {}; @@ -9177,6 +9698,7 @@ * with true if wifi password was cached and false if it was unavailable. * @param {string} ssid * @param {function(boolean): void} callback + * @return {undefined} */ chrome.gcdPrivate.prefetchWifiPassword = function(ssid, callback) {}; @@ -9189,6 +9711,7 @@ * |status|: The status of the operation (success or type of error). * |deviceInfo|: Content of /privet/info response. * https://developers.google.com/cloud-devices/v1/reference/local-api/info + * @return {undefined} */ chrome.gcdPrivate.getDeviceInfo = function(serviceName, callback) {}; @@ -9201,6 +9724,7 @@ * |sessionId|, is the session ID (identifies the session for future calls). * 2nd param, |status|, is the status (success or type of error). 3rd param, * |pairingTypes|, is a list of pairing types supported by this device. + * @return {undefined} */ chrome.gcdPrivate.createSession = function(serviceName, callback) {}; @@ -9210,6 +9734,7 @@ * @param {number} sessionId * @param {string} pairingType * @param {function(string): void} callback + * @return {undefined} */ chrome.gcdPrivate.startPairing = function(sessionId, pairingType, callback) {}; @@ -9219,6 +9744,7 @@ * @param {number} sessionId * @param {string} code * @param {function(string): void} callback + * @return {undefined} */ chrome.gcdPrivate.confirmCode = function(sessionId, code, callback) {}; @@ -9233,6 +9759,7 @@ * @param {!Object} input The input message to be sent over the encrypted * channel. * @param {function(string, ?Object): void} callback + * @return {undefined} */ chrome.gcdPrivate.sendMessage = function(sessionId, api, input, callback) {}; @@ -9240,6 +9767,7 @@ /** * Terminate the session with the device. * @param {number} sessionId + * @return {undefined} */ chrome.gcdPrivate.terminateSession = function(sessionId) {}; @@ -9248,6 +9776,7 @@ * Returns command definitions. * @param {string} deviceId The device to get command definitions for. * @param {function(!Object): void} callback The result callback. + * @return {undefined} */ chrome.gcdPrivate.getCommandDefinitions = function(deviceId, callback) {}; @@ -9262,6 +9791,7 @@ * @param {!Object} command Described at * https://developers.google.com/cloud-devices/v1/reference/commands. * @param {function(!Object): void} callback The result callback. + * @return {undefined} */ chrome.gcdPrivate.insertCommand = function( deviceId, expireInMs, command, callback) {}; @@ -9271,6 +9801,7 @@ * Returns a particular command. * @param {string} commandId Unique command ID. * @param {function(!Object): void} callback The result callback. + * @return {undefined} */ chrome.gcdPrivate.getCommand = function(commandId, callback) {}; @@ -9279,6 +9810,7 @@ * Cancels a command. * @param {string} commandId Unique command ID. * @param {function(!Object): void} callback The result callback. + * @return {undefined} */ chrome.gcdPrivate.cancelCommand = function(commandId, callback) {}; @@ -9290,6 +9822,7 @@ * value 'me' can be used to list by the current user. * @param {string} state Command state. * @param {function(!Array<!Object>): void} callback The result callback. + * @return {undefined} */ chrome.gcdPrivate.getCommandsList = function( deviceId, byUser, state, callback) {}; @@ -9303,11 +9836,17 @@ chrome.gcdPrivate.DeviceEvent = function() {}; -/** @param {function(!chrome.gcdPrivate.Device): void} callback */ +/** + * @param {function(!chrome.gcdPrivate.Device): void} callback + * @return {undefined} + */ chrome.gcdPrivate.DeviceEvent.prototype.addListener = function(callback) {}; -/** @param {function(!chrome.gcdPrivate.Device): void} callback */ +/** + * @param {function(!chrome.gcdPrivate.Device): void} callback + * @return {undefined} + */ chrome.gcdPrivate.DeviceEvent.prototype.removeListener = function(callback) {}; @@ -9396,6 +9935,7 @@ /** * @param {!chrome.bluetoothPrivate.NewAdapterState} adapterState * @param {function()} callback + * @return {undefined} */ chrome.bluetoothPrivate.setAdapterState = function(adapterState, callback) {}; @@ -9403,10 +9943,37 @@ /** * @param {!chrome.bluetoothPrivate.SetPairingResponseOptions} options * @param {function()} callback + * @return {undefined} */ chrome.bluetoothPrivate.setPairingResponse = function(options, callback) {}; +/** + * @param {string} deviceAddress + * @param {function():void=} callback + * @return {undefined} + */ +chrome.bluetoothPrivate.forgetDevice = function(deviceAddress, callback) {}; + + +/** + * @typedef {{ + * transport: (!chrome.bluetoothPrivate.TransportType|undefined), + * uuids: ((string|!Array<string>)|undefined), + * rssi: (number|undefined), + * pathloss: (number|undefined) + * }} + */ +chrome.bluetoothPrivate.DiscoveryFilter; + + +/** + * Set or clear discovery filter. + * @param {!chrome.bluetoothPrivate.DiscoveryFilter} discoveryFilter + * @param {function():void=} callback + */ +chrome.bluetoothPrivate.setDiscoveryFilter = function(discoveryFilter, callback) {}; + /** * Event whose listeners take a PairingEvent parameter. @@ -9415,12 +9982,18 @@ chrome.bluetoothPrivate.PairingEventEvent = function() {}; -/** @param {function(!chrome.bluetoothPrivate.PairingEvent): void} callback */ +/** + * @param {function(!chrome.bluetoothPrivate.PairingEvent): void} callback + * @return {undefined} + */ chrome.bluetoothPrivate.PairingEventEvent.prototype.addListener = function(callback) {}; -/** @param {function(!chrome.bluetoothPrivate.PairingEvent): void} callback */ +/** + * @param {function(!chrome.bluetoothPrivate.PairingEvent): void} callback + * @return {undefined} + */ chrome.bluetoothPrivate.PairingEventEvent.prototype.removeListener = function(callback) {}; @@ -9525,6 +10098,7 @@ * @param {function(string, string): void=} opt_callback Response callback that * returns two string: (1) an error string (or empty string on success) and * (2) an error code in case of error + * @return {undefined} */ chrome.inlineInstallPrivate.install = function(id, opt_callback) {}; @@ -9558,6 +10132,481 @@ * thumbnail: (boolean|undefined) * }} details * @param {function(ArrayBuffer=)} callback + * @return {undefined} * @see https://goo.gl/7dvJFW#method-setWallpaper */ - chrome.wallpaper.setWallpaper = function(details, callback) {}; +chrome.wallpaper.setWallpaper = function(details, callback) {}; + + +/** + * @const + * @see https://developer.chrome.com/extensions/downloads + */ +chrome.downloads = {}; + + +/** + * @enum {string} + * @see https://developer.chrome.com/extensions/downloads#type-FilenameConflictAction + */ +chrome.downloads.FilenameConflictAction = { + UNIQUIFY: '', + OVERWRITE: '', + PROMPT: '' +}; + + +/** + * @enum {string} + * @see https://developer.chrome.com/extensions/downloads#type-InterruptReason + */ +chrome.downloads.InterruptReason = { + FILE_FAILED: '', + FILE_ACCESS_DENIED: '', + FILE_NO_SPACE: '', + FILE_NAME_TOO_LONG: '', + FILE_TOO_LARGE: '', + FILE_VIRUS_INFECTED: '', + FILE_TRANSIENT_ERROR: '', + FILE_BLOCKED: '', + FILE_SECURITY_CHECK_FAILED: '', + FILE_TOO_SHORT: '', + FILE_HASH_MISMATCH: '', + NETWORK_FAILED: '', + NETWORK_TIMEOUT: '', + NETWORK_DISCONNECTED: '', + NETWORK_SERVER_DOWN: '', + NETWORK_INVALID_REQUEST: '', + SERVER_FAILED: '', + SERVER_NO_RANGE: '', + SERVER_BAD_CONTENT: '', + SERVER_UNAUTHORIZED: '', + SERVER_CERT_PROBLEM: '', + SERVER_FORBIDDEN: '', + SERVER_UNREACHABLE: '', + USER_CANCELED: '', + USER_SHUTDOWN: '', + CRASH: '', +}; + + +/** + * @enum {string} + * @see https://developer.chrome.com/extensions/downloads#type-DangerType + */ +chrome.downloads.DangerType = { + FILE: '', + URL: '', + CONTENT: '', + UNCOMMON: '', + HOST: '', + UNWANTED: '', + SAFE: '', + ACCEPTED: '' +}; + + +/** + * @enum {string} + * @see https://developer.chrome.com/extensions/downloads#type-State + */ +chrome.downloads.State = { + IN_PROGRESS: '', + INTERRUPTED: '', + COMPLETE: '', +}; + + +/** + * @constructor + * @see https://developer.chrome.com/extensions/downloads#type-DownloadItem + */ +chrome.downloads.DownloadItem = function() {}; + + +/** @type {number} */ +chrome.downloads.DownloadItem.prototype.id; + + +/** @type {string} */ +chrome.downloads.DownloadItem.prototype.url; + + +/** @type {string} */ +chrome.downloads.DownloadItem.prototype.finalUrl; + + +/** @type {string} */ +chrome.downloads.DownloadItem.prototype.referrer; + + +/** @type {string} */ +chrome.downloads.DownloadItem.prototype.filename; + + +/** @type {boolean} */ +chrome.downloads.DownloadItem.prototype.incognito; + + +/** @type {!chrome.downloads.DangerType|string} */ +chrome.downloads.DownloadItem.prototype.dangerType; + + +/** @type {string} */ +chrome.downloads.DownloadItem.prototype.mime; + + +/** @type {string} */ +chrome.downloads.DownloadItem.prototype.startTime; + + +/** @type {?string} */ +chrome.downloads.DownloadItem.prototype.endTime; + + +/** @type {?string} */ +chrome.downloads.DownloadItem.prototype.estimatedEndTime; + + +/** @type {!chrome.downloads.State|string} */ +chrome.downloads.DownloadItem.prototype.state; + + +/** @type {boolean} */ +chrome.downloads.DownloadItem.prototype.paused; + + +/** @type {boolean} */ +chrome.downloads.DownloadItem.prototype.canResume; + + +/** @type {!chrome.downloads.InterruptReason|string|undefined} */ +chrome.downloads.DownloadItem.prototype.error; + + +/** @type {number} */ +chrome.downloads.DownloadItem.prototype.bytesReceived; + + +/** @type {number} */ +chrome.downloads.DownloadItem.prototype.totalBytes; + + +/** @type {number} */ +chrome.downloads.DownloadItem.prototype.fileSize; + + +/** @type {boolean} */ +chrome.downloads.DownloadItem.prototype.exists; + + +/** @type {?string} */ +chrome.downloads.DownloadItem.prototype.byExtensionId; + + +/** @type {?string} */ +chrome.downloads.DownloadItem.prototype.byExtensionName; + + +/** + * @constructor + * @see https://developer.chrome.com/extensions/downloads#type-StringDelta + */ +chrome.downloads.StringDelta = function() {}; + + +/** @type {?string} */ +chrome.downloads.StringDelta.prototype.previous; + + +/** @type {?string} */ +chrome.downloads.StringDelta.prototype.current; + + +/** + * @constructor + * @see https://developer.chrome.com/extensions/downloads#type-DoubleDelta + */ +chrome.downloads.DoubleDelta = function() {}; + + +/** @type {?number} */ +chrome.downloads.DoubleDelta.prototype.previous; + + +/** @type {?number} */ +chrome.downloads.DoubleDelta.prototype.current; + + +/** + * @constructor + * @see https://developer.chrome.com/extensions/downloads#type-BooleanDelta + */ +chrome.downloads.BooleanDelta = function() {}; + + +/** @type {?boolean} */ +chrome.downloads.BooleanDelta.prototype.previous; + + +/** @type {?boolean} */ +chrome.downloads.BooleanDelta.prototype.current; + + +/** + * @param {{ + * url: string, + * filename: (string|undefined), + * conflictAction: + * (!chrome.downloads.FilenameConflictAction|string|undefined), + * saveAs: (boolean|undefined), + * method: (string|undefined), + * headers: (!Array<{name:string, value:string}>|undefined), + * body: (string|undefined) + * }} details + * @param {function(number)=} opt_callback + * @see https://developer.chrome.com/extensions/downloads#method-download + */ +chrome.downloads.download = function(details, opt_callback) {}; + + +/** + * @typedef {?{ + * query: (!Array<string>|undefined), + * startedBefore: (string|undefined), + * startedAfter: (string|undefined), + * endedBefore: (string|undefined), + * endedAfter: (string|undefined), + * totalBytesGreater: (number|undefined), + * totalBytesLess: (number|undefined), + * filenameRegex: (string|undefined), + * urlRegex: (string|undefined), + * finalUrlRegex: (string|undefined), + * limit: (number|undefined), + * orderedBy: (!Array<string>|undefined), + * id: (number|undefined), + * url: (string|undefined), + * finalUrl: (string|undefined), + * filename: (string|undefined), + * danger: (!chrome.downloads.DangerType|string|undefined), + * mime: (string|undefined), + * startTime: (string|undefined), + * endTime: (string|undefined), + * state: (!chrome.downloads.State|string|undefined), + * paused: (boolean|undefined), + * error: (!chrome.downloads.InterruptReason|string|undefined), + * bytesReceived: (number|undefined), + * totalBytes: (number|undefined), + * fileSize: (number|undefined), + * exists: (boolean|undefined) + * }} + */ +chrome.downloads.Query; + + +/** + * @param {!chrome.downloads.Query} query + * @param {function(!Array<!chrome.downloads.DownloadItem>)} callback + * @see https://developer.chrome.com/extensions/downloads#method-search + */ +chrome.downloads.search = function(query, callback) {}; + + +/** + * @param {number} id + * @param {function()=} opt_callback + * + * @see https://developer.chrome.com/extensions/downloads#method-pause + */ +chrome.downloads.pause = function(id, opt_callback) {}; + + +/** + * @param {string} id + * @param {function()=} opt_callback + * + * @see https://developer.chrome.com/extensions/downloads#method-resume + */ +chrome.downloads.resume = function(id, opt_callback) {}; + + +/** + * @param {number} id + * @param {function()=} opt_callback + * + * @see https://developer.chrome.com/extensions/downloads#method-cancel + */ +chrome.downloads.cancel = function(id, opt_callback) {}; + + +/** + * @param {number} id + * @param {{size:(number|undefined)}|function(string)} optionsOrCallback + * @param {function(string)=} opt_callback + * + * @see https://developer.chrome.com/extensions/downloads#method-getFileIcon + */ +chrome.downloads.getFileIcon = function(id, optionsOrCallback, opt_callback) {}; + + +/** + * @param {number} id + * @see https://developer.chrome.com/extensions/downloads#method-open + */ +chrome.downloads.open = function(id) {}; + + +/** + * @param {number} id + * @see https://developer.chrome.com/extensions/downloads#method-show + */ +chrome.downloads.show = function(id) {}; + + +/** @see https://developer.chrome.com/extensions/downloads#method-showDefaultFolder */ +chrome.downloads.showDefaultFolder = function() {}; + + +/** + * @param {!chrome.downloads.Query} query + * @param {function(!Array<number>)} callback + * @see https://developer.chrome.com/extensions/downloads#method-erase + */ +chrome.downloads.erase = function(query, callback) {}; + + +/** + * @param {number} id + * @param {function()=} opt_callback + * + * @see https://developer.chrome.com/extensions/downloads#method-removeFile + */ +chrome.downloads.removeFile = function(id, opt_callback) {}; + + +/** + * @param {number} id + * @param {function()=} opt_callback + * + * @see https://developer.chrome.com/extensions/downloads#method-acceptDanger + */ +chrome.downloads.acceptDanger = function(id, opt_callback) {}; + + +/** + * @param {number} id + * + * @see https://developer.chrome.com/extensions/downloads#method-drag + */ +chrome.downloads.drag = function(id) {}; + + +/** + * @param {boolean} enabled + * + * @see https://developer.chrome.com/extensions/downloads#method-setShelfEnabled + */ +chrome.downloads.setShelfEnabled = function(enabled) {}; + + +/** + * @constructor + * @see https://developer.chrome.com/extensions/downloads#event-onCreated + */ +chrome.downloads.CreatedEvent = function() {}; + + +/** @param {function(!chrome.downloads.DownloadItem)} callback */ +chrome.downloads.CreatedEvent.prototype.addListener = function(callback) {}; + + +/** @param {function(!chrome.downloads.DownloadItem)} callback */ +chrome.downloads.CreatedEvent.prototype.removeListener = + function(callback) {}; + + +/** + * @param {function(!chrome.downloads.DownloadItem)} callback + * @return {boolean} + */ +chrome.downloads.CreatedEvent.prototype.hasListener = function(callback) {}; + + +/** @return {boolean} */ +chrome.downloads.CreatedEvent.prototype.hasListeners = function() {}; + + +/** + * @type {!chrome.downloads.CreatedEvent} + * https://developer.chrome.com/extensions/downloads#event-onCreated + */ +chrome.downloads.onCreated; + + +/** + * @type {!ChromeNumberEvent} + * https://developer.chrome.com/extensions/downloads#event-onErased + */ +chrome.downloads.onErased; + + +/** + * @type {!ChromeObjectEvent} + * https://developer.chrome.com/extensions/downloads#event-onChanged + */ +chrome.downloads.onChanged; + + +/** + * @typedef {?{ + * filename: string, + * conflictAction: (!chrome.downloads.FilenameConflictAction|undefined) + * }} + * @see https://developer.chrome.com/extensions/downloads#event-onDeterminingFilename + */ +chrome.downloads.FilenameSuggestion; + + +/** + * @constructor + * @see https://developer.chrome.com/extensions/downloads#event-onDeterminingFilename + */ +chrome.downloads.DeterminingFilenameEvent = function() {}; + + +/** + * @param {function( + * !chrome.downloads.DownloadItem, + * function(!chrome.downloads.FilenameSuggestion=))} callback */ +chrome.downloads.DeterminingFilenameEvent.prototype.addListener = + function(callback) {}; + + +/** + * @param {function( + * !chrome.downloads.DownloadItem, + * function(!chrome.downloads.FilenameSuggestion=))} callback */ +chrome.downloads.DeterminingFilenameEvent.prototype.removeListener = + function(callback) {}; + + +/** + * @param {function( + * !chrome.downloads.DownloadItem, + * function(!chrome.downloads.FilenameSuggestion=))} callback + * @return {boolean} + */ +chrome.downloads.DeterminingFilenameEvent.prototype.hasListener = + function(callback) {}; + + +/** @return {boolean} */ +chrome.downloads.DeterminingFilenameEvent.prototype.hasListeners = + function() {}; + + +/** + * @type {!chrome.downloads.DeterminingFilenameEvent} + * https://developer.chrome.com/extensions/downloads#event-onDeterminingFilename + */ +chrome.downloads.onDeterminingFilename;
diff --git a/third_party/closure_compiler/externs/polymer-1.0.js b/third_party/closure_compiler/externs/polymer-1.0.js index 7b4101f..6d6acb6 100644 --- a/third_party/closure_compiler/externs/polymer-1.0.js +++ b/third_party/closure_compiler/externs/polymer-1.0.js
@@ -44,6 +44,10 @@ * additional IP rights grant found at http://polymer.github.io/PATENTS.txt. */ +if (Math.random() < 1) { + throw "polymer externs should not be executed"; +} + /** * @param {!{is: string}} descriptor The Polymer descriptor of the element. * @see https://github.com/Polymer/polymer/blob/0.8-preview/PRIMER.md#custom-element-registration @@ -868,6 +872,13 @@ PolymerDomApi.prototype.queryDistributedElements = function(selector) {}; /** + * Returns a list of effective child nodes for this element. + * + * @return {!Array<!HTMLElement>} + */ +PolymerDomApi.prototype.getEffectiveChildNodes = function() {}; + +/** * A Polymer Event API. * * @constructor @@ -1156,6 +1167,16 @@ DomRepeatElement.prototype.indexForElement = function(el) {}; +/** + * Count of currently rendered items after `filter` (if any) has been applied. + * If "chunking mode" is enabled, `renderedItemCount` is updated each time a + * set of template instances is rendered. + * + * @type {number} + */ +DomRepeatElement.prototype.renderedItemCount; + + /** * @see https://github.com/Polymer/polymer/blob/master/src/lib/template/array-selector.html @@ -1342,3 +1363,83 @@ * @param {...*} args The function arguments. */ Polymer.RenderStatus.afterNextRender = function(element, fn, args) {} + + + +/** + * Static analysis for Polymer. + * @type {!Object} + */ +var hydrolysis = {}; + +/** + * A database of Polymer metadata defined in HTML + * @param {boolean} attachAST If true, attach a parse5 compliant AST + * @param {Object=} opt_loader An optional FileLoader used to load + * external resources + */ +hydrolysis.Analyzer = function(attachAST, opt_loader) {}; + + +/** + * Shorthand for transitively loading and processing all imports + * beginning at href. + * @param {string} href The root import to begin loading from. + * @param {Object=} opt_options Any additional options for the load. + */ +hydrolysis.Analyzer.analyze = function(href, opt_options) {}; + + + +/** + * Contains information useful for debugging. Should not be used in production + * code and the API may change on short notice. + * @type {!Object} + */ +Polymer.telemetry; + +/** + * Number of elements instantiated so far. + * @type {number} + */ +Polymer.telemetry.instanceCount; + +/** + * Array of all registered element prototypes. Being prototypes, not all runtime + * properties will be available, but eg. `is` is always there. + * @type {!Array<!PolymerElement>} + */ +Polymer.telemetry.registrations; + +Polymer.AppLayout; + +/** @constructor */ +Polymer.AppLayout.LocalDomWithBackground = function(){}; +/** @type {!HTMLElement} */ +Polymer.AppLayout.LocalDomWithBackground.prototype.backgroundFrontLayer; +/** @type {!HTMLElement} */ +Polymer.AppLayout.LocalDomWithBackground.prototype.backgroundRearLayer; +/** @type {!HTMLElement} */ +Polymer.AppLayout.LocalDomWithBackground.prototype.background; + +/** + * @constructor + * @extends {PolymerElement} + */ +Polymer.AppLayout.ElementWithBackground = function(){}; + +// TODO(ajo): Follow up with app-layout team and remove private api from this prototype +Polymer.AppLayout.ElementWithBackground.prototype = { + /** @type {!Polymer.AppLayout.LocalDomWithBackground} */ + $: null, + /** @return {boolean} True if there's content below the current element */ + isContentBelow: function(){}, + /** Updates the elements scroll state */ + _updateScrollState: function(){}, + /** @return {boolean} true if the element is on screen */ + isOnScreen: function(){}, + /** @type {number} Internal bookkeeping to track screen position */ + _deltaHeight: 0, + /** @return {?Element} Element in local dom by id. */ + _getDOMRef: function(title){} +}
diff --git a/third_party/closure_compiler/runner/runner.jar b/third_party/closure_compiler/runner/runner.jar index e6e6091..5e12b00 100644 --- a/third_party/closure_compiler/runner/runner.jar +++ b/third_party/closure_compiler/runner/runner.jar Binary files differ
diff --git a/third_party/dpkg-dev/LICENSE b/third_party/dpkg-dev/LICENSE new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/third_party/dpkg-dev/LICENSE
@@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License.
diff --git a/third_party/dpkg-dev/OWNERS b/third_party/dpkg-dev/OWNERS new file mode 100644 index 0000000..6204ee1 --- /dev/null +++ b/third_party/dpkg-dev/OWNERS
@@ -0,0 +1,3 @@ +thomasanderson@chromium.org +thestig@chromium.org +sbc@chromium.org
diff --git a/third_party/dpkg-dev/README.chromium b/third_party/dpkg-dev/README.chromium new file mode 100644 index 0000000..56e1186b --- /dev/null +++ b/third_party/dpkg-dev/README.chromium
@@ -0,0 +1,23 @@ +Name: dpkg-dev +URL: http://packages.ubuntu.com/source/trusty/dpkg +Version: 1.17.5ubuntu5.6 +Date: October 13, 2016 +License: GPL v2 +License File: NOT_SHIPPED +Security Critical: no + +Description: +Dpkg is a package manager for Debian. The component necessary for +development is dpkg-shlibdeps which calculates dependency information +for executables. + +This should only be added during the infra transition from Precise to +Trusty, and should be removed once complete. See bug 655755 for +details. + +How to update: +1. Acquire the dpkg-dev source: "apt-get source dpkg-dev". +2. Remove all but *table and scripts/ from the top-level dirctory. +3. Remove all but "Dpkg", "Dpkg.pm", and "dpkg-shlibdeps.pl" from scripts/ + +Local Modifications: Removed all unnecessary utilities.
diff --git a/third_party/dpkg-dev/abitable b/third_party/dpkg-dev/abitable new file mode 100644 index 0000000..300a1ab --- /dev/null +++ b/third_party/dpkg-dev/abitable
@@ -0,0 +1,11 @@ +# This file contains the table of arch ABI attribute overrides. +# +# If the ABI is not present here then the attribute information for a +# Debian triplet matches the one on the cputable. +# +# Column 1 is the Debian name for the ABI. +# Column 2 is the size (in bits) of the ABI integers/pointers. +# +# <Debian name> <Bits> +gnuabin32 32 +gnux32 32
diff --git a/third_party/dpkg-dev/cputable b/third_party/dpkg-dev/cputable new file mode 100644 index 0000000..fb5d2764 --- /dev/null +++ b/third_party/dpkg-dev/cputable
@@ -0,0 +1,42 @@ +# This file contains the table of known CPU names. +# +# Architecture names are formed as a combination of the system name +# (from ostable) and CPU name (from this table) after mapping from +# the Debian triplet (from triplettable). +# +# Column 1 is the Debian name for the CPU, used to form the cpu part in +# the Debian triplet. +# Column 2 is the GNU name for the CPU, used to output build and host +# targets in ‘dpkg-architecture’. +# Column 3 is an extended regular expression used to match against the +# CPU part of the output of the GNU config.guess script. +# Column 4 is the size (in bits) of the integers/pointers +# Column 5 is the endianness (byte ordering in numbers) +# +# <Debian name> <GNU name> <config.guess regex> <Bits> <Endianness> +i386 i486 (i[3456]86|pentium) 32 little +ia64 ia64 ia64 64 little +alpha alpha alpha.* 64 little +amd64 x86_64 x86_64 64 little +armeb armeb arm.*b 32 big +arm arm arm.* 32 little +arm64 aarch64 aarch64 64 little +avr32 avr32 avr32 32 big +hppa hppa hppa.* 32 big +m32r m32r m32r 32 big +m68k m68k m68k 32 big +mips mips mips(eb)? 32 big +mipsel mipsel mipsel 32 little +mips64 mips64 mips64 64 big +mips64el mips64el mips64el 64 little +powerpc powerpc (powerpc|ppc) 32 big +ppc64 powerpc64 (powerpc|ppc)64 64 big +ppc64el powerpc64le powerpc64le 64 little +s390 s390 s390 32 big +s390x s390x s390x 64 big +sh3 sh3 sh3 32 little +sh3eb sh3eb sh3eb 32 big +sh4 sh4 sh4 32 little +sh4eb sh4eb sh4eb 32 big +sparc sparc sparc 32 big +sparc64 sparc64 sparc64 64 big
diff --git a/third_party/dpkg-dev/ostable b/third_party/dpkg-dev/ostable new file mode 100644 index 0000000..29d3843 --- /dev/null +++ b/third_party/dpkg-dev/ostable
@@ -0,0 +1,38 @@ +# This file contains the table of known operating system names. +# +# Architecture names are formed as a combination of the system name +# (from this table) and CPU name (from cputable) after mapping from +# the Debian triplet (from triplettable). +# +# Column 1 is the Debian name for the system, used to form the system part +# in the Debian triplet. +# Column 2 is the GNU name for the system, used to output build and host +# targets in ‘dpkg-architecture’. +# Column 3 is an extended regular expression used to match against the +# system part of the output of the GNU config.guess script. +# +# <Debian name> <GNU name> <config.guess regex> +uclibceabi-linux linux-uclibceabi linux[^-]*-uclibceabi +uclibc-linux linux-uclibc linux[^-]*-uclibc +musleabihf-linux linux-musleabihf linux[^-]*-musleabihf +musl-linux linux-musl linux[^-]*-musl +gnueabihf-linux linux-gnueabihf linux[^-]*-gnueabihf +gnueabi-linux linux-gnueabi linux[^-]*-gnueabi +gnuabin32-linux linux-gnuabin32 linux[^-]*-gnuabin32 +gnuabi64-linux linux-gnuabi64 linux[^-]*-gnuabi64 +gnuspe-linux linux-gnuspe linux[^-]*-gnuspe +gnux32-linux linux-gnux32 linux[^-]*-gnux32 +gnulp-linux linux-gnulp linux[^-]*-gnulp +gnu-linux linux-gnu linux[^-]*(-gnu.*)? +gnu-kfreebsd kfreebsd-gnu kfreebsd[^-]*(-gnu.*)? +gnu-knetbsd knetbsd-gnu knetbsd[^-]*(-gnu.*)? +gnu-kopensolaris kopensolaris-gnu kopensolaris[^-]*(-gnu.*)? +gnu-hurd gnu gnu[^-]* +bsd-darwin darwin darwin[^-]* +bsd-freebsd freebsd freebsd[^-]* +bsd-netbsd netbsd netbsd[^-]* +bsd-openbsd openbsd openbsd[^-]* +sysv-solaris solaris solaris[^-]* +uclibceabi-uclinux uclinux-uclibceabi uclinux[^-]*-uclibceabi +uclibc-uclinux uclinux-uclibc uclinux[^-]*(-uclibc.*)? +tos-mint mint mint[^-]*
diff --git a/third_party/dpkg-dev/scripts/Dpkg.pm b/third_party/dpkg-dev/scripts/Dpkg.pm new file mode 100644 index 0000000..2dd93989 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg.pm
@@ -0,0 +1,43 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg; + +use strict; +use warnings; + +our $VERSION = '1.01'; + +use Exporter qw(import); +our @EXPORT_OK = qw($PROGNAME $PROGVERSION $CONFDIR $ADMINDIR $LIBDIR $DATADIR); +our @EXPORT = qw($version $progname $admindir $dpkglibdir $pkgdatadir); + +our ($PROGNAME) = $0 =~ m{(?:.*/)?([^/]*)}; + +# The following lines are automatically fixed at install time +our $PROGVERSION = '1.17.x'; +our $CONFDIR = '/etc/dpkg'; +our $ADMINDIR = '/var/lib/dpkg'; +our $LIBDIR = '.'; +our $DATADIR = '..'; +$DATADIR = $ENV{DPKG_DATADIR} if defined $ENV{DPKG_DATADIR}; + +# XXX: Backwards compatibility, to be removed on VERSION 2.00. +## no critic (Variables::ProhibitPackageVars) +our $version = $PROGVERSION; +our $admindir = $ADMINDIR; +our $dpkglibdir = $LIBDIR; +our $pkgdatadir = $DATADIR; +## use critic + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Arch.pm b/third_party/dpkg-dev/scripts/Dpkg/Arch.pm new file mode 100644 index 0000000..33a669d8 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Arch.pm
@@ -0,0 +1,445 @@ +# Copyright © 2006-2013 Guillem Jover <guillem@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Arch; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use Exporter qw(import); +our @EXPORT_OK = qw(get_raw_build_arch get_raw_host_arch + get_build_arch get_host_arch get_gcc_host_gnu_type + get_valid_arches debarch_eq debarch_is debarch_is_wildcard + debarch_to_cpuattrs + debarch_to_gnutriplet gnutriplet_to_debarch + debtriplet_to_gnutriplet gnutriplet_to_debtriplet + debtriplet_to_debarch debarch_to_debtriplet + gnutriplet_to_multiarch debarch_to_multiarch); + +use POSIX qw(:errno_h); +use Dpkg (); +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Util qw(:list); +use Dpkg::BuildEnv; + +my (@cpu, @os); +my (%cputable, %ostable); +my (%cputable_re, %ostable_re); +my (%cpubits, %cpuendian); +my %abibits; + +my %debtriplet_to_debarch; +my %debarch_to_debtriplet; + +{ + my $build_arch; + my $host_arch; + my $gcc_host_gnu_type; + + sub get_raw_build_arch() + { + return $build_arch if defined $build_arch; + + # Note: We *always* require an installed dpkg when inferring the + # build architecture. The bootstrapping case is handled by + # dpkg-architecture itself, by avoiding computing the DEB_BUILD_ + # variables when they are not requested. + + $build_arch = `dpkg --print-architecture`; + syserr('dpkg --print-architecture failed') if $? >> 8; + + chomp $build_arch; + return $build_arch; + } + + sub get_build_arch() + { + return Dpkg::BuildEnv::get('DEB_BUILD_ARCH') || get_raw_build_arch(); + } + + sub get_gcc_host_gnu_type() + { + return $gcc_host_gnu_type if defined $gcc_host_gnu_type; + + $gcc_host_gnu_type = `\${CC:-gcc} -dumpmachine`; + if ($? >> 8) { + $gcc_host_gnu_type = ''; + } else { + chomp $gcc_host_gnu_type; + } + + return $gcc_host_gnu_type; + } + + sub get_raw_host_arch() + { + return $host_arch if defined $host_arch; + + $gcc_host_gnu_type = get_gcc_host_gnu_type(); + + if ($gcc_host_gnu_type eq '') { + warning(_g("couldn't determine gcc system type, falling back to " . + 'default (native compilation)')); + } else { + my (@host_archtriplet) = gnutriplet_to_debtriplet($gcc_host_gnu_type); + $host_arch = debtriplet_to_debarch(@host_archtriplet); + + if (defined $host_arch) { + $gcc_host_gnu_type = debtriplet_to_gnutriplet(@host_archtriplet); + } else { + warning(_g('unknown gcc system type %s, falling back to ' . + 'default (native compilation)'), $gcc_host_gnu_type); + $gcc_host_gnu_type = ''; + } + } + + if (!defined($host_arch)) { + # Switch to native compilation. + $host_arch = get_raw_build_arch(); + } + + return $host_arch; + } + + sub get_host_arch() + { + return Dpkg::BuildEnv::get('DEB_HOST_ARCH') || get_raw_host_arch(); + } +} + +sub get_valid_arches() +{ + read_cputable(); + read_ostable(); + + my @arches; + + foreach my $os (@os) { + foreach my $cpu (@cpu) { + my $arch = debtriplet_to_debarch(split(/-/, $os, 2), $cpu); + push @arches, $arch if defined($arch); + } + } + + return @arches; +} + +my $cputable_loaded = 0; +sub read_cputable +{ + return if ($cputable_loaded); + + local $_; + local $/ = "\n"; + + open my $cputable_fh, '<', "$Dpkg::DATADIR/cputable" + or syserr(_g('cannot open %s'), 'cputable'); + while (<$cputable_fh>) { + if (m/^(?!\#)(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/) { + $cputable{$1} = $2; + $cputable_re{$1} = $3; + $cpubits{$1} = $4; + $cpuendian{$1} = $5; + push @cpu, $1; + } + } + close $cputable_fh; + + $cputable_loaded = 1; +} + +my $ostable_loaded = 0; +sub read_ostable +{ + return if ($ostable_loaded); + + local $_; + local $/ = "\n"; + + open my $ostable_fh, '<', "$Dpkg::DATADIR/ostable" + or syserr(_g('cannot open %s'), 'ostable'); + while (<$ostable_fh>) { + if (m/^(?!\#)(\S+)\s+(\S+)\s+(\S+)/) { + $ostable{$1} = $2; + $ostable_re{$1} = $3; + push @os, $1; + } + } + close $ostable_fh; + + $ostable_loaded = 1; +} + +my $abitable_loaded = 0; +sub abitable_load() +{ + return if ($abitable_loaded); + + local $_; + local $/ = "\n"; + + # Because the abitable is only for override information, do not fail if + # it does not exist, as that will only mean the other tables do not have + # an entry needing to be overridden. This way we do not require a newer + # dpkg by libdpkg-perl. + if (open my $abitable_fh, '<', "$Dpkg::DATADIR/abitable") { + while (<$abitable_fh>) { + if (m/^(?!\#)(\S+)\s+(\S+)/) { + $abibits{$1} = $2; + } + } + close $abitable_fh; + } elsif ($! != ENOENT) { + syserr(_g('cannot open %s'), 'abitable'); + } + + $abitable_loaded = 1; +} + +my $triplettable_loaded = 0; +sub read_triplettable() +{ + return if ($triplettable_loaded); + + read_cputable(); + + local $_; + local $/ = "\n"; + + open my $triplettable_fh, '<', "$Dpkg::DATADIR/triplettable" + or syserr(_g('cannot open %s'), 'triplettable'); + while (<$triplettable_fh>) { + if (m/^(?!\#)(\S+)\s+(\S+)/) { + my $debtriplet = $1; + my $debarch = $2; + + if ($debtriplet =~ /<cpu>/) { + foreach my $_cpu (@cpu) { + (my $dt = $debtriplet) =~ s/<cpu>/$_cpu/; + (my $da = $debarch) =~ s/<cpu>/$_cpu/; + + next if exists $debarch_to_debtriplet{$da} + or exists $debtriplet_to_debarch{$dt}; + + $debarch_to_debtriplet{$da} = $dt; + $debtriplet_to_debarch{$dt} = $da; + } + } else { + $debarch_to_debtriplet{$2} = $1; + $debtriplet_to_debarch{$1} = $2; + } + } + } + close $triplettable_fh; + + $triplettable_loaded = 1; +} + +sub debtriplet_to_gnutriplet(@) +{ + my ($abi, $os, $cpu) = @_; + + read_cputable(); + read_ostable(); + + return unless defined($abi) && defined($os) && defined($cpu) && + exists($cputable{$cpu}) && exists($ostable{"$abi-$os"}); + return join('-', $cputable{$cpu}, $ostable{"$abi-$os"}); +} + +sub gnutriplet_to_debtriplet($) +{ + my ($gnu) = @_; + return unless defined($gnu); + my ($gnu_cpu, $gnu_os) = split(/-/, $gnu, 2); + return unless defined($gnu_cpu) && defined($gnu_os); + + read_cputable(); + read_ostable(); + + my ($os, $cpu); + + foreach my $_cpu (@cpu) { + if ($gnu_cpu =~ /^$cputable_re{$_cpu}$/) { + $cpu = $_cpu; + last; + } + } + + foreach my $_os (@os) { + if ($gnu_os =~ /^(.*-)?$ostable_re{$_os}$/) { + $os = $_os; + last; + } + } + + return if !defined($cpu) || !defined($os); + return (split(/-/, $os, 2), $cpu); +} + +sub gnutriplet_to_multiarch($) +{ + my ($gnu) = @_; + my ($cpu, $cdr) = split(/-/, $gnu, 2); + + if ($cpu =~ /^i[456]86$/) { + return "i386-$cdr"; + } else { + return $gnu; + } +} + +sub debarch_to_multiarch($) +{ + my ($arch) = @_; + + return gnutriplet_to_multiarch(debarch_to_gnutriplet($arch)); +} + +sub debtriplet_to_debarch(@) +{ + my ($abi, $os, $cpu) = @_; + + read_triplettable(); + + if (!defined($abi) || !defined($os) || !defined($cpu)) { + return; + } elsif (exists $debtriplet_to_debarch{"$abi-$os-$cpu"}) { + return $debtriplet_to_debarch{"$abi-$os-$cpu"}; + } else { + return; + } +} + +sub debarch_to_debtriplet($) +{ + local ($_) = @_; + my $arch; + + read_triplettable(); + + if (/^linux-([^-]*)/) { + # XXX: Might disappear in the future, not sure yet. + $arch = $1; + } else { + $arch = $_; + } + + my $triplet = $debarch_to_debtriplet{$arch}; + + if (defined($triplet)) { + return split(/-/, $triplet, 3); + } else { + return; + } +} + +sub debarch_to_gnutriplet($) +{ + my ($arch) = @_; + + return debtriplet_to_gnutriplet(debarch_to_debtriplet($arch)); +} + +sub gnutriplet_to_debarch($) +{ + my ($gnu) = @_; + + return debtriplet_to_debarch(gnutriplet_to_debtriplet($gnu)); +} + +sub debwildcard_to_debtriplet($) +{ + my ($arch) = @_; + my @tuple = split /-/, $arch, 3; + + if (any { $_ eq 'any' } @tuple) { + if (scalar @tuple == 3) { + return @tuple; + } elsif (scalar @tuple == 2) { + return ('any', @tuple); + } else { + return ('any', 'any', 'any'); + } + } else { + return debarch_to_debtriplet($arch); + } +} + +sub debarch_to_cpuattrs($) +{ + my ($arch) = @_; + my ($abi, $os, $cpu) = debarch_to_debtriplet($arch); + + if (defined($cpu)) { + abitable_load(); + + return ($abibits{$abi} || $cpubits{$cpu}, $cpuendian{$cpu}); + } else { + return; + } +} + +sub debarch_eq($$) +{ + my ($a, $b) = @_; + + return 1 if ($a eq $b); + + my @a = debarch_to_debtriplet($a); + my @b = debarch_to_debtriplet($b); + + return 0 if scalar @a != 3 or scalar @b != 3; + + return ($a[0] eq $b[0] && $a[1] eq $b[1] && $a[2] eq $b[2]); +} + +sub debarch_is($$) +{ + my ($real, $alias) = @_; + + return 1 if ($alias eq $real or $alias eq 'any'); + + my @real = debarch_to_debtriplet($real); + my @alias = debwildcard_to_debtriplet($alias); + + return 0 if scalar @real != 3 or scalar @alias != 3; + + if (($alias[0] eq $real[0] || $alias[0] eq 'any') && + ($alias[1] eq $real[1] || $alias[1] eq 'any') && + ($alias[2] eq $real[2] || $alias[2] eq 'any')) { + return 1; + } + + return 0; +} + +sub debarch_is_wildcard($) +{ + my ($arch) = @_; + + return 0 if $arch eq 'all'; + + my @triplet = debwildcard_to_debtriplet($arch); + + return 0 if scalar @triplet != 3; + return 1 if any { $_ eq 'any' } @triplet; + return 0; +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/BuildEnv.pm b/third_party/dpkg-dev/scripts/Dpkg/BuildEnv.pm new file mode 100644 index 0000000..2615d5d --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/BuildEnv.pm
@@ -0,0 +1,107 @@ +# Copyright © 2012 Guillem Jover <guillem@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::BuildEnv; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +my %env_modified = (); +my %env_accessed = (); + +=encoding utf8 + +=head1 NAME + +Dpkg::BuildEnv - track build environment + +=head1 DESCRIPTION + +The Dpkg::BuildEnv module is used by dpkg-buildflags to track the build +environment variables being used and modified. + +=head1 FUNCTIONS + +=over 4 + +=item $bf->set($varname, $value) + +Update the build environment variable $varname with value $value. Record +it as being accessed and modified. + +=cut + +sub set { + my ($varname, $value) = @_; + $env_modified{$varname} = 1; + $env_accessed{$varname} = 1; + $ENV{$varname} = $value; +} + +=item $bf->get($varname) + +Get the build environment variable $varname value. Record it as being +accessed. + +=cut + +sub get { + my ($varname) = @_; + $env_accessed{$varname} = 1; + return $ENV{$varname}; +} + +=item $bf->has($varname) + +Return a boolean indicating whether the environment variable exists. +Record it as being accessed. + +=cut + +sub has { + my ($varname) = @_; + $env_accessed{$varname} = 1; + return exists $ENV{$varname}; +} + +=item my @list = $bf->list_accessed() + +Returns a list of all environment variables that have been accessed. + +=cut + +sub list_accessed { + my @list = sort keys %env_accessed; + return @list; +} + +=item my @list = $bf->list_modified() + +Returns a list of all environment variables that have been modified. + +=cut + +sub list_modified { + my @list = sort keys %env_modified; + return @list; +} + +=back + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/BuildFlags.pm b/third_party/dpkg-dev/scripts/Dpkg/BuildFlags.pm new file mode 100644 index 0000000..162e803 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/BuildFlags.pm
@@ -0,0 +1,456 @@ +# Copyright © 2010-2011 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::BuildFlags; + +use strict; +use warnings; + +our $VERSION = '1.03'; + +use Dpkg (); +use Dpkg::Gettext; +use Dpkg::BuildEnv; +use Dpkg::BuildOptions; +use Dpkg::ErrorHandling; +use Dpkg::Vendor qw(run_vendor_hook); + +=encoding utf8 + +=head1 NAME + +Dpkg::BuildFlags - query build flags + +=head1 DESCRIPTION + +The Dpkg::BuildFlags object is used by dpkg-buildflags and can be used +to query the same information. + +=head1 FUNCTIONS + +=over 4 + +=item my $bf = Dpkg::BuildFlags->new() + +Create a new Dpkg::BuildFlags object. It will be initialized based +on the value of several configuration files and environment variables. + +=cut + +sub new { + my ($this, %opts) = @_; + my $class = ref($this) || $this; + + my $self = { + }; + bless $self, $class; + $self->load_vendor_defaults(); + return $self; +} + +=item $bf->load_vendor_defaults() + +Reset the flags stored to the default set provided by the vendor. + +=cut + +sub load_vendor_defaults { + my ($self) = @_; + $self->{options} = {}; + $self->{source} = {}; + $self->{features} = {}; + my $build_opts = Dpkg::BuildOptions->new(); + $self->{build_options} = $build_opts; + my $default_flags = $build_opts->has('noopt') ? '-g -O0' : '-g -O2'; + $self->{flags} = { + CPPFLAGS => '', + CFLAGS => $default_flags, + CXXFLAGS => $default_flags, + GCJFLAGS => $default_flags, + FFLAGS => $default_flags, + LDFLAGS => '', + }; + $self->{origin} = { + CPPFLAGS => 'vendor', + CFLAGS => 'vendor', + CXXFLAGS => 'vendor', + GCJFLAGS => 'vendor', + FFLAGS => 'vendor', + LDFLAGS => 'vendor', + }; + $self->{maintainer} = { + CPPFLAGS => 0, + CFLAGS => 0, + CXXFLAGS => 0, + GCJFLAGS => 0, + FFLAGS => 0, + LDFLAGS => 0, + }; + # The Debian vendor hook will add hardening build flags + run_vendor_hook('update-buildflags', $self); +} + +=item $bf->load_system_config() + +Update flags from the system configuration. + +=cut + +sub load_system_config { + my ($self) = @_; + $self->update_from_conffile("$Dpkg::CONFDIR/buildflags.conf", 'system'); +} + +=item $bf->load_user_config() + +Update flags from the user configuration. + +=cut + +sub load_user_config { + my ($self) = @_; + my $confdir = $ENV{XDG_CONFIG_HOME}; + $confdir ||= $ENV{HOME} . '/.config' if defined $ENV{HOME}; + if (defined $confdir) { + $self->update_from_conffile("$confdir/dpkg/buildflags.conf", 'user'); + } +} + +=item $bf->load_environment_config() + +Update flags based on user directives stored in the environment. See +dpkg-buildflags(1) for details. + +=cut + +sub load_environment_config { + my ($self) = @_; + foreach my $flag (keys %{$self->{flags}}) { + my $envvar = 'DEB_' . $flag . '_SET'; + if (Dpkg::BuildEnv::has($envvar)) { + $self->set($flag, Dpkg::BuildEnv::get($envvar), 'env'); + } + $envvar = 'DEB_' . $flag . '_STRIP'; + if (Dpkg::BuildEnv::has($envvar)) { + $self->strip($flag, Dpkg::BuildEnv::get($envvar), 'env'); + } + $envvar = 'DEB_' . $flag . '_APPEND'; + if (Dpkg::BuildEnv::has($envvar)) { + $self->append($flag, Dpkg::BuildEnv::get($envvar), 'env'); + } + $envvar = 'DEB_' . $flag . '_PREPEND'; + if (Dpkg::BuildEnv::has($envvar)) { + $self->prepend($flag, Dpkg::BuildEnv::get($envvar), 'env'); + } + } +} + +=item $bf->load_maintainer_config() + +Update flags based on maintainer directives stored in the environment. See +dpkg-buildflags(1) for details. + +=cut + +sub load_maintainer_config { + my ($self) = @_; + foreach my $flag (keys %{$self->{flags}}) { + my $envvar = 'DEB_' . $flag . '_MAINT_SET'; + if (Dpkg::BuildEnv::has($envvar)) { + $self->set($flag, Dpkg::BuildEnv::get($envvar), undef, 1); + } + $envvar = 'DEB_' . $flag . '_MAINT_STRIP'; + if (Dpkg::BuildEnv::has($envvar)) { + $self->strip($flag, Dpkg::BuildEnv::get($envvar), undef, 1); + } + $envvar = 'DEB_' . $flag . '_MAINT_APPEND'; + if (Dpkg::BuildEnv::has($envvar)) { + $self->append($flag, Dpkg::BuildEnv::get($envvar), undef, 1); + } + $envvar = 'DEB_' . $flag . '_MAINT_PREPEND'; + if (Dpkg::BuildEnv::has($envvar)) { + $self->prepend($flag, Dpkg::BuildEnv::get($envvar), undef, 1); + } + } +} + + +=item $bf->load_config() + +Call successively load_system_config(), load_user_config(), +load_environment_config() and load_maintainer_config() to update the +default build flags defined by the vendor. + +=cut + +sub load_config { + my ($self) = @_; + $self->load_system_config(); + $self->load_user_config(); + $self->load_environment_config(); + $self->load_maintainer_config(); +} + +=item $bf->set($flag, $value, $source, $maint) + +Update the build flag $flag with value $value and record its origin as +$source (if defined). Record it as maintainer modified if $maint is +defined and true. + +=cut + +sub set { + my ($self, $flag, $value, $src, $maint) = @_; + $self->{flags}->{$flag} = $value; + $self->{origin}->{$flag} = $src if defined $src; + $self->{maintainer}->{$flag} = $maint if $maint; +} + +=item $bf->set_feature($area, $feature, $enabled) + +Update the boolean state of whether a specific feature within a known +feature area has been enabled. The only currently known feature area is +"hardening". + +=cut + +sub set_feature { + my ($self, $area, $feature, $enabled) = @_; + $self->{features}{$area}{$feature} = $enabled; +} + +=item $bf->strip($flag, $value, $source, $maint) + +Update the build flag $flag by stripping the flags listed in $value and +record its origin as $source (if defined). Record it as maintainer modified +if $maint is defined and true. + +=cut + +sub strip { + my ($self, $flag, $value, $src, $maint) = @_; + foreach my $tostrip (split(/\s+/, $value)) { + next unless length $tostrip; + $self->{flags}->{$flag} =~ s/(^|\s+)\Q$tostrip\E(\s+|$)/ /g; + } + $self->{flags}->{$flag} =~ s/^\s+//g; + $self->{flags}->{$flag} =~ s/\s+$//g; + $self->{origin}->{$flag} = $src if defined $src; + $self->{maintainer}->{$flag} = $maint if $maint; +} + +=item $bf->append($flag, $value, $source, $maint) + +Append the options listed in $value to the current value of the flag $flag. +Record its origin as $source (if defined). Record it as maintainer modified +if $maint is defined and true. + +=cut + +sub append { + my ($self, $flag, $value, $src, $maint) = @_; + if (length($self->{flags}->{$flag})) { + $self->{flags}->{$flag} .= " $value"; + } else { + $self->{flags}->{$flag} = $value; + } + $self->{origin}->{$flag} = $src if defined $src; + $self->{maintainer}->{$flag} = $maint if $maint; +} + +=item $bf->prepend($flag, $value, $source, $maint) + +Prepend the options listed in $value to the current value of the flag $flag. +Record its origin as $source (if defined). Record it as maintainer modified +if $maint is defined and true. + +=cut + +sub prepend { + my ($self, $flag, $value, $src, $maint) = @_; + if (length($self->{flags}->{$flag})) { + $self->{flags}->{$flag} = "$value " . $self->{flags}->{$flag}; + } else { + $self->{flags}->{$flag} = $value; + } + $self->{origin}->{$flag} = $src if defined $src; + $self->{maintainer}->{$flag} = $maint if $maint; +} + + +=item $bf->update_from_conffile($file, $source) + +Update the current build flags based on the configuration directives +contained in $file. See dpkg-buildflags(1) for the format of the directives. + +$source is the origin recorded for any build flag set or modified. + +=cut + +sub update_from_conffile { + my ($self, $file, $src) = @_; + return unless -e $file; + open(my $conf_fh, '<', $file) or syserr(_g('cannot read %s'), $file); + while (<$conf_fh>) { + chomp; + next if /^\s*#/; # Skip comments + next if /^\s*$/; # Skip empty lines + if (/^(append|prepend|set|strip)\s+(\S+)\s+(\S.*\S)\s*$/i) { + my ($op, $flag, $value) = ($1, $2, $3); + unless (exists $self->{flags}->{$flag}) { + warning(_g('line %d of %s mentions unknown flag %s'), $., $file, $flag); + $self->{flags}->{$flag} = ''; + } + if (lc($op) eq 'set') { + $self->set($flag, $value, $src); + } elsif (lc($op) eq 'strip') { + $self->strip($flag, $value, $src); + } elsif (lc($op) eq 'append') { + $self->append($flag, $value, $src); + } elsif (lc($op) eq 'prepend') { + $self->prepend($flag, $value, $src); + } + } else { + warning(_g('line %d of %s is invalid, it has been ignored'), $., $file); + } + } + close($conf_fh); +} + +=item $bf->get($flag) + +Return the value associated to the flag. It might be undef if the +flag doesn't exist. + +=cut + +sub get { + my ($self, $key) = @_; + return $self->{flags}{$key}; +} + +=item $bf->get_feature_areas() + +Return the feature areas (i.e. the area values has_features will return +true for). + +=cut + +sub get_feature_areas { + my ($self) = @_; + return keys %{$self->{features}}; +} + +=item $bf->get_features($area) + +Return, for the given area, a hash with keys as feature names, and values +as booleans indicating whether the feature is enabled or not. + +=cut + +sub get_features { + my ($self, $area) = @_; + return %{$self->{features}{$area}}; +} + +=item $bf->get_origin($flag) + +Return the origin associated to the flag. It might be undef if the +flag doesn't exist. + +=cut + +sub get_origin { + my ($self, $key) = @_; + return $self->{origin}{$key}; +} + +=item $bf->is_maintainer_modified($flag) + +Return true if the flag is modified by the maintainer. + +=cut + +sub is_maintainer_modified { + my ($self, $key) = @_; + return $self->{maintainer}{$key}; +} + +=item $bf->has_features($area) + +Returns true if the given area of features is known, and false otherwise. +The only currently recognized area is "hardening". + +=cut + +sub has_features { + my ($self, $area) = @_; + return exists $self->{features}{$area}; +} + +=item $bf->has($option) + +Returns a boolean indicating whether the flags exists in the object. + +=cut + +sub has { + my ($self, $key) = @_; + return exists $self->{flags}{$key}; +} + +=item my @flags = $bf->list() + +Returns the list of flags stored in the object. + +=cut + +sub list { + my ($self) = @_; + my @list = sort keys %{$self->{flags}}; + return @list; +} + +=back + +=head1 CHANGES + +=head2 Version 1.01 + +New method: $bf->prepend() very similar to append(). Implement support of +the prepend operation everywhere. + +New method: $bf->load_maintainer_config() that update the build flags +based on the package maintainer directives. + +=head2 Version 1.02 + +New methods: $bf->get_features(), $bf->has_features(), $bf->set_feature(). + +=head2 Version 1.03 + +New method: $bf->get_feature_areas() to list possible values for +$bf->get_features. + +New method $bf->is_maintainer_modified() and new optional parameter to +$bf->set(), $bf->append(), $bf->prepend(), $bf->strip(). + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org> + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/BuildOptions.pm b/third_party/dpkg-dev/scripts/Dpkg/BuildOptions.pm new file mode 100644 index 0000000..90213aa --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/BuildOptions.pm
@@ -0,0 +1,204 @@ +# Copyright © 2007 Frank Lichtenheld <djpig@debian.org> +# Copyright © 2010 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::BuildOptions; + +use strict; +use warnings; + +our $VERSION = '1.01'; + +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::BuildEnv; + +=encoding utf8 + +=head1 NAME + +Dpkg::BuildOptions - parse and update build options + +=head1 DESCRIPTION + +The Dpkg::BuildOptions object can be used to manipulate options stored +in environment variables like DEB_BUILD_OPTIONS and +DEB_BUILD_MAINT_OPTIONS. + +=head1 FUNCTIONS + +=over 4 + +=item my $bo = Dpkg::BuildOptions->new(%opts) + +Create a new Dpkg::BuildOptions object. It will be initialized based +on the value of the environment variable named $opts{envvar} (or +DEB_BUILD_OPTIONS if that option is not set). + +=cut + +sub new { + my ($this, %opts) = @_; + my $class = ref($this) || $this; + + my $self = { + options => {}, + source => {}, + envvar => $opts{envvar} // 'DEB_BUILD_OPTIONS', + }; + bless $self, $class; + $self->merge(Dpkg::BuildEnv::get($self->{envvar}), $self->{envvar}); + return $self; +} + +=item $bo->reset() + +Reset the object to not have any option (it's empty). + +=cut + +sub reset { + my ($self) = @_; + $self->{options} = {}; + $self->{source} = {}; +} + +=item $bo->merge($content, $source) + +Merge the options set in $content and record that they come from the +source $source. $source is mainly used in warning messages currently +to indicate where invalid options have been detected. + +$content is a space separated list of options with optional assigned +values like "nocheck parallel=2". + +=cut + +sub merge { + my ($self, $content, $source) = @_; + return 0 unless defined $content; + my $count = 0; + foreach (split(/\s+/, $content)) { + unless (/^([a-z][a-z0-9_-]*)(?:=(\S*))?$/) { + warning(_g('invalid flag in %s: %s'), $source, $_); + next; + } + $count += $self->set($1, $2, $source); + } + return $count; +} + +=item $bo->set($option, $value, [$source]) + +Store the given option in the objet with the given value. It's legitimate +for a value to be undefined if the option is a simple boolean (its +presence means true, its absence means false). The $source is optional +and indicates where the option comes from. + +The known options have their values checked for sanity. Options without +values have their value removed and options with invalid values are +discarded. + +=cut + +sub set { + my ($self, $key, $value, $source) = @_; + + # Sanity checks + if ($key =~ /^(noopt|nostrip|nocheck)$/ && defined($value)) { + $value = undef; + } elsif ($key eq 'parallel') { + $value //= ''; + return 0 if $value !~ /^\d*$/; + } + + $self->{options}{$key} = $value; + $self->{source}{$key} = $source; + + return 1; +} + +=item $bo->get($option) + +Return the value associated to the option. It might be undef even if the +option exists. You might want to check with $bo->has($option) to verify if +the option is stored in the object. + +=cut + +sub get { + my ($self, $key) = @_; + return $self->{options}{$key}; +} + +=item $bo->has($option) + +Returns a boolean indicating whether the option is stored in the object. + +=cut + +sub has { + my ($self, $key) = @_; + return exists $self->{options}{$key}; +} + +=item $string = $bo->output($fh) + +Return a string representation of the build options suitable to be +assigned to an environment variable. Can optionnaly output that string to +the given filehandle. + +=cut + +sub output { + my ($self, $fh) = @_; + my $o = $self->{options}; + my $res = join(' ', map { defined($o->{$_}) ? $_ . '=' . $o->{$_} : $_ } sort keys %$o); + print { $fh } $res if defined $fh; + return $res; +} + +=item $bo->export([$var]) + +Export the build options to the given environment variable. If omitted, +the environment variable defined at creation time is assumed. The value +set to the variable is also returned. + +=cut + +sub export { + my ($self, $var) = @_; + $var = $self->{envvar} unless defined $var; + my $content = $self->output(); + Dpkg::BuildEnv::set($var, $content); + return $content; +} + +=back + +=head1 CHANGES + +=head2 Version 1.01 + +Enable to use another environment variable instead of DEB_BUILD_OPTIONS. +Thus add support for the "envvar" option at creation time. + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org> + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/BuildProfiles.pm b/third_party/dpkg-dev/scripts/Dpkg/BuildProfiles.pm new file mode 100644 index 0000000..98c86d36 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/BuildProfiles.pm
@@ -0,0 +1,82 @@ +# Copyright © 2013 Guillem Jover <guillem@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::BuildProfiles; + +use strict; +use warnings; + +our $VERSION = '0.01'; +our @EXPORT_OK = qw(get_build_profiles set_build_profiles); + +use Exporter qw(import); + +use Dpkg::BuildEnv; + +my $cache_profiles; +my @build_profiles; + +=encoding utf8 + +=head1 NAME + +Dpkg::BuildProfiles - handle build profiles + +=head1 DESCRIPTION + +The Dpkg::BuildProfiles module provides functions to handle the build +profiles. + +=head1 FUNCTIONS + +=over 4 + +=item my @profiles = get_build_profiles() + +Get an array with the currently active build profiles, taken from +the environment variable B<DEB_BUILD_PROFILES>. + +=cut + +sub get_build_profiles { + return @build_profiles if $cache_profiles; + + if (Dpkg::BuildEnv::has('DEB_BUILD_PROFILES')) { + @build_profiles = split / /, Dpkg::BuildEnv::get('DEB_BUILD_PROFILES'); + } + $cache_profiles = 1; + + return @build_profiles; +} + +=item set_build_profiles(@profiles) + +Set C<@profiles> as the current active build profiles, by setting +the environment variable B<DEB_BUILD_PROFILES>. + +=cut + +sub set_build_profiles { + my (@profiles) = @_; + + @build_profiles = @profiles; + Dpkg::BuildEnv::set('DEB_BUILD_PROFILES', join ' ', @profiles); +} + +=back + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Changelog.pm b/third_party/dpkg-dev/scripts/Dpkg/Changelog.pm new file mode 100644 index 0000000..40e0628e --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Changelog.pm
@@ -0,0 +1,704 @@ +# Copyright © 2005, 2007 Frank Lichtenheld <frank@lichtenheld.de> +# Copyright © 2009 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +=encoding utf8 + +=head1 NAME + +Dpkg::Changelog - base class to implement a changelog parser + +=head1 DESCRIPTION + +Dpkg::Changelog is a class representing a changelog file +as an array of changelog entries (Dpkg::Changelog::Entry). +By deriving this object and implementing its parse method, you +add the ability to fill this object with changelog entries. + +=head2 FUNCTIONS + +=cut + +package Dpkg::Changelog; + +use strict; +use warnings; + +our $VERSION = '1.00'; + +use Dpkg; +use Dpkg::Gettext; +use Dpkg::ErrorHandling qw(:DEFAULT report); +use Dpkg::Control; +use Dpkg::Control::Changelog; +use Dpkg::Control::Fields; +use Dpkg::Index; +use Dpkg::Version; +use Dpkg::Vendor qw(run_vendor_hook); + +use parent qw(Dpkg::Interface::Storable); + +use overload + '@{}' => sub { return $_[0]->{data} }; + +=over 4 + +=item my $c = Dpkg::Changelog->new(%options) + +Creates a new changelog object. + +=cut + +sub new { + my ($this, %opts) = @_; + my $class = ref($this) || $this; + my $self = { + verbose => 1, + parse_errors => [] + }; + bless $self, $class; + $self->set_options(%opts); + return $self; +} + +=item $c->load($filename) + +Parse $filename as a changelog. + +=cut + +=item $c->set_options(%opts) + +Change the value of some options. "verbose" (defaults to 1) defines +whether parse errors are displayed as warnings by default. "reportfile" +is a string to use instead of the name of the file parsed, in particular +in error messages. "range" defines the range of entries that we want to +parse, the parser will stop as soon as it has parsed enough data to +satisfy $c->get_range($opts{range}). + +=cut + +sub set_options { + my ($self, %opts) = @_; + $self->{$_} = $opts{$_} foreach keys %opts; +} + +=item $c->reset_parse_errors() + +Can be used to delete all information about errors occurred during +previous L<parse> runs. + +=cut + +sub reset_parse_errors { + my ($self) = @_; + $self->{parse_errors} = []; +} + +=item $c->parse_error($file, $line_nr, $error, [$line]) + +Record a new parse error in $file at line $line_nr. The error message is +specified with $error and a copy of the line can be recorded in $line. + +=cut + +sub parse_error { + my ($self, $file, $line_nr, $error, $line) = @_; + shift; + + push @{$self->{parse_errors}}, [ @_ ]; + + if ($self->{verbose}) { + if ($line) { + warning("%20s(l$line_nr): $error\nLINE: $line", $file); + } else { + warning("%20s(l$line_nr): $error", $file); + } + } +} + +=item $c->get_parse_errors() + +Returns all error messages from the last L<parse> run. +If called in scalar context returns a human readable +string representation. If called in list context returns +an array of arrays. Each of these arrays contains + +=over 4 + +=item 1. + +a string describing the origin of the data (a filename usually). If the +reportfile configuration option was given, its value will be used instead. + +=item 2. + +the line number where the error occurred + +=item 3. + +an error description + +=item 4. + +the original line + +=back + +=cut + +sub get_parse_errors { + my ($self) = @_; + + if (wantarray) { + return @{$self->{parse_errors}}; + } else { + my $res = ''; + foreach my $e (@{$self->{parse_errors}}) { + if ($e->[3]) { + $res .= report(_g('warning'),_g("%s(l%s): %s\nLINE: %s"), @$e ); + } else { + $res .= report(_g('warning'), _g('%s(l%s): %s'), @$e); + } + } + return $res; + } +} + +=item $c->set_unparsed_tail($tail) + +Add a string representing unparsed lines after the changelog entries. +Use undef as $tail to remove the unparsed lines currently set. + +=item $c->get_unparsed_tail() + +Return a string representing the unparsed lines after the changelog +entries. Returns undef if there's no such thing. + +=cut + +sub set_unparsed_tail { + my ($self, $tail) = @_; + $self->{unparsed_tail} = $tail; +} + +sub get_unparsed_tail { + my ($self) = @_; + return $self->{unparsed_tail}; +} + +=item @{$c} + +Returns all the Dpkg::Changelog::Entry objects contained in this changelog +in the order in which they have been parsed. + +=item $c->get_range($range) + +Returns an array (if called in list context) or a reference to an array of +Dpkg::Changelog::Entry objects which each represent one entry of the +changelog. $range is a hash reference describing the range of entries +to return. See section L<"RANGE SELECTION">. + +=cut + +sub __sanity_check_range { + my ($self, $r) = @_; + my $data = $self->{data}; + + if (defined($r->{offset}) and not defined($r->{count})) { + warning(_g("'offset' without 'count' has no effect")) if $self->{verbose}; + delete $r->{offset}; + } + + ## no critic (ControlStructures::ProhibitUntilBlocks) + if ((defined($r->{count}) || defined($r->{offset})) && + (defined($r->{from}) || defined($r->{since}) || + defined($r->{to}) || defined($r->{until}))) + { + warning(_g("you can't combine 'count' or 'offset' with any other " . + 'range option')) if $self->{verbose}; + delete $r->{from}; + delete $r->{since}; + delete $r->{to}; + delete $r->{until}; + } + if (defined($r->{from}) && defined($r->{since})) { + warning(_g("you can only specify one of 'from' and 'since', using " . + "'since'")) if $self->{verbose}; + delete $r->{from}; + } + if (defined($r->{to}) && defined($r->{until})) { + warning(_g("you can only specify one of 'to' and 'until', using " . + "'until'")) if $self->{verbose}; + delete $r->{to}; + } + + # Handle non-existing versions + my (%versions, @versions); + foreach my $entry (@{$data}) { + $versions{$entry->get_version()->as_string()} = 1; + push @versions, $entry->get_version()->as_string(); + } + if ((defined($r->{since}) and not exists $versions{$r->{since}})) { + warning(_g("'%s' option specifies non-existing version"), 'since'); + warning(_g('use newest entry that is earlier than the one specified')); + foreach my $v (@versions) { + if (version_compare_relation($v, REL_LT, $r->{since})) { + $r->{since} = $v; + last; + } + } + if (not exists $versions{$r->{since}}) { + # No version was earlier, include all + warning(_g('none found, starting from the oldest entry')); + delete $r->{since}; + $r->{from} = $versions[-1]; + } + } + if ((defined($r->{from}) and not exists $versions{$r->{from}})) { + warning(_g("'%s' option specifies non-existing version"), 'from'); + warning(_g('use oldest entry that is later than the one specified')); + my $oldest; + foreach my $v (@versions) { + if (version_compare_relation($v, REL_GT, $r->{from})) { + $oldest = $v; + } + } + if (defined($oldest)) { + $r->{from} = $oldest; + } else { + warning(_g("no such entry found, ignoring '%s' parameter"), 'from'); + delete $r->{from}; # No version was oldest + } + } + if (defined($r->{until}) and not exists $versions{$r->{until}}) { + warning(_g("'%s' option specifies non-existing version"), 'until'); + warning(_g('use oldest entry that is later than the one specified')); + my $oldest; + foreach my $v (@versions) { + if (version_compare_relation($v, REL_GT, $r->{until})) { + $oldest = $v; + } + } + if (defined($oldest)) { + $r->{until} = $oldest; + } else { + warning(_g("no such entry found, ignoring '%s' parameter"), 'until'); + delete $r->{until}; # No version was oldest + } + } + if (defined($r->{to}) and not exists $versions{$r->{to}}) { + warning(_g("'%s' option specifies non-existing version"), 'to'); + warning(_g('use newest entry that is earlier than the one specified')); + foreach my $v (@versions) { + if (version_compare_relation($v, REL_LT, $r->{to})) { + $r->{to} = $v; + last; + } + } + if (not exists $versions{$r->{to}}) { + # No version was earlier + warning(_g("no such entry found, ignoring '%s' parameter"), 'to'); + delete $r->{to}; + } + } + + if (defined($r->{since}) and $data->[0]->get_version() eq $r->{since}) { + warning(_g("'since' option specifies most recent version, ignoring")); + delete $r->{since}; + } + if (defined($r->{until}) and $data->[-1]->get_version() eq $r->{until}) { + warning(_g("'until' option specifies oldest version, ignoring")); + delete $r->{until}; + } + ## use critic +} + +sub get_range { + my ($self, $range) = @_; + $range //= {}; + my $res = $self->_data_range($range); + if (defined $res) { + return @$res if wantarray; + return $res; + } else { + return; + } +} + +sub _is_full_range { + my ($self, $range) = @_; + + return 1 if $range->{all}; + + # If no range delimiter is specified, we want everything. + foreach (qw(since until from to count offset)) { + return 0 if exists $range->{$_}; + } + + return 1; +} + +sub _data_range { + my ($self, $range) = @_; + + my $data = $self->{data} or return; + + return [ @$data ] if $self->_is_full_range($range); + + $self->__sanity_check_range($range); + + my ($start, $end); + if (defined($range->{count})) { + my $offset = $range->{offset} || 0; + my $count = $range->{count}; + # Convert count/offset in start/end + if ($offset > 0) { + $offset -= ($count < 0); + } elsif ($offset < 0) { + $offset = $#$data + ($count > 0) + $offset; + } else { + $offset = $#$data if $count < 0; + } + $start = $end = $offset; + $start += $count+1 if $count < 0; + $end += $count-1 if $count > 0; + # Check limits + $start = 0 if $start < 0; + return if $start > $#$data; + $end = $#$data if $end > $#$data; + return if $end < 0; + $end = $start if $end < $start; + return [ @{$data}[$start .. $end] ]; + } + + ## no critic (ControlStructures::ProhibitUntilBlocks) + my @result; + my $include = 1; + $include = 0 if defined($range->{to}) or defined($range->{until}); + foreach (@$data) { + my $v = $_->get_version(); + $include = 1 if defined($range->{to}) and $v eq $range->{to}; + last if defined($range->{since}) and $v eq $range->{since}; + + push @result, $_ if $include; + + $include = 1 if defined($range->{until}) and $v eq $range->{until}; + last if defined($range->{from}) and $v eq $range->{from}; + } + ## use critic + + return \@result if scalar(@result); + return; +} + +=item $c->abort_early() + +Returns true if enough data have been parsed to be able to return all +entries selected by the range set at creation (or with set_options). + +=cut + +sub abort_early { + my ($self) = @_; + + my $data = $self->{data} or return; + my $r = $self->{range} or return; + my $count = $r->{count} || 0; + my $offset = $r->{offset} || 0; + + return if $self->_is_full_range($r); + return if $offset < 0 or $count < 0; + if (defined($r->{count})) { + if ($offset > 0) { + $offset -= ($count < 0); + } + my $start = my $end = $offset; + $end += $count-1 if $count > 0; + return ($start < @$data and $end < @$data); + } + + return unless defined($r->{since}) or defined($r->{from}); + foreach (@$data) { + my $v = $_->get_version(); + return 1 if defined($r->{since}) and $v eq $r->{since}; + return 1 if defined($r->{from}) and $v eq $r->{from}; + } + + return; +} + +=item $c->save($filename) + +Save the changelog in the given file. + +=item $c->output() + +=item "$c" + +Returns a string representation of the changelog (it's a concatenation of +the string representation of the individual changelog entries). + +=item $c->output($fh) + +Output the changelog to the given filehandle. + +=cut + +sub output { + my ($self, $fh) = @_; + my $str = ''; + foreach my $entry (@{$self}) { + my $text = $entry->output(); + print { $fh } $text if defined $fh; + $str .= $text if defined wantarray; + } + my $text = $self->get_unparsed_tail(); + if (defined $text) { + print { $fh } $text if defined $fh; + $str .= $text if defined wantarray; + } + return $str; +} + +=item my $control = $c->dpkg($range) + +Returns a Dpkg::Control::Changelog object representing the entries selected +by the optional range specifier (see L<"RANGE SELECTION"> for details). +Returns undef in no entries are matched. + +The following fields are contained in the object: + +=over 4 + +=item Source + +package name (in the first entry) + +=item Version + +packages' version (from first entry) + +=item Distribution + +target distribution (from first entry) + +=item Urgency + +urgency (highest of all printed entries) + +=item Maintainer + +person that created the (first) entry + +=item Date + +date of the (first) entry + +=item Closes + +bugs closed by the entry/entries, sorted by bug number + +=item Changes + +content of the the entry/entries + +=back + +=cut + +our ( @URGENCIES, %URGENCIES ); +BEGIN { + @URGENCIES = qw(low medium high critical emergency); + my $i = 1; + %URGENCIES = map { $_ => $i++ } @URGENCIES; +} + +sub dpkg { + my ($self, $range) = @_; + + my @data = $self->get_range($range) or return; + my $src = shift @data; + + my $f = Dpkg::Control::Changelog->new(); + $f->{Urgency} = $src->get_urgency() || 'unknown'; + $f->{Source} = $src->get_source() || 'unknown'; + $f->{Version} = $src->get_version() // 'unknown'; + $f->{Distribution} = join(' ', $src->get_distributions()); + $f->{Maintainer} = $src->get_maintainer() || ''; + $f->{Date} = $src->get_timestamp() || ''; + $f->{Changes} = $src->get_dpkg_changes(); + + # handle optional fields + my $opts = $src->get_optional_fields(); + my %closes; + foreach (keys %$opts) { + if (/^Urgency$/i) { # Already dealt + } elsif (/^Closes$/i) { + $closes{$_} = 1 foreach (split(/\s+/, $opts->{Closes})); + } else { + field_transfer_single($opts, $f); + } + } + + foreach my $bin (@data) { + my $oldurg = $f->{Urgency} || ''; + my $oldurgn = $URGENCIES{$f->{Urgency}} || -1; + my $newurg = $bin->get_urgency() || ''; + my $newurgn = $URGENCIES{$newurg} || -1; + $f->{Urgency} = ($newurgn > $oldurgn) ? $newurg : $oldurg; + $f->{Changes} .= "\n" . $bin->get_dpkg_changes(); + + # handle optional fields + $opts = $bin->get_optional_fields(); + foreach (keys %$opts) { + if (/^Closes$/i) { + $closes{$_} = 1 foreach (split(/\s+/, $opts->{Closes})); + } elsif (not exists $f->{$_}) { # Don't overwrite an existing field + field_transfer_single($opts, $f); + } + } + } + + if (scalar keys %closes) { + $f->{Closes} = join ' ', sort { $a <=> $b } keys %closes; + } + run_vendor_hook('post-process-changelog-entry', $f); + + return $f; +} + +=item my @controls = $c->rfc822($range) + +Returns a Dpkg::Index containing Dpkg::Control::Changelog objects where +each object represents one entry in the changelog that is part of the +range requested (see L<"RANGE SELECTION"> for details). For the format of +such an object see the description of the L<"dpkg"> method (while ignoring +the remarks about which values are taken from the first entry). + +=cut + +sub rfc822 { + my ($self, $range) = @_; + + my @data = $self->get_range($range) or return; + my $index = Dpkg::Index->new(type => CTRL_CHANGELOG); + + foreach my $entry (@data) { + my $f = Dpkg::Control::Changelog->new(); + $f->{Urgency} = $entry->get_urgency() || 'unknown'; + $f->{Source} = $entry->get_source() || 'unknown'; + $f->{Version} = $entry->get_version() // 'unknown'; + $f->{Distribution} = join(' ', $entry->get_distributions()); + $f->{Maintainer} = $entry->get_maintainer() || ''; + $f->{Date} = $entry->get_timestamp() || ''; + $f->{Changes} = $entry->get_dpkg_changes(); + + # handle optional fields + my $opts = $entry->get_optional_fields(); + foreach (keys %$opts) { + field_transfer_single($opts, $f) unless exists $f->{$_}; + } + + run_vendor_hook('post-process-changelog-entry', $f); + + $index->add($f); + } + return $index; +} + +=back + +=head1 RANGE SELECTION + +A range selection is described by a hash reference where +the allowed keys and values are described below. + +The following options take a version number as value. + +=over 4 + +=item since + +Causes changelog information from all versions strictly +later than B<version> to be used. + +=item until + +Causes changelog information from all versions strictly +earlier than B<version> to be used. + +=item from + +Similar to C<since> but also includes the information for the +specified B<version> itself. + +=item to + +Similar to C<until> but also includes the information for the +specified B<version> itself. + +=back + +The following options don't take version numbers as values: + +=over 4 + +=item all + +If set to a true value, all entries of the changelog are returned, +this overrides all other options. + +=item count + +Expects a signed integer as value. Returns C<value> entries from the +top of the changelog if set to a positive integer, and C<abs(value)> +entries from the tail if set to a negative integer. + +=item offset + +Expects a signed integer as value. Changes the starting point for +C<count>, either counted from the top (positive integer) or from +the tail (negative integer). C<offset> has no effect if C<count> +wasn't given as well. + +=back + +Some examples for the above options. Imagine an example changelog with +entries for the versions 1.2, 1.3, 2.0, 2.1, 2.2, 3.0 and 3.1. + + Range Included entries + C<{ since =E<gt> '2.0' }> 3.1, 3.0, 2.2 + C<{ until =E<gt> '2.0' }> 1.3, 1.2 + C<{ from =E<gt> '2.0' }> 3.1, 3.0, 2.2, 2.1, 2.0 + C<{ to =E<gt> '2.0' }> 2.0, 1.3, 1.2 + C<{ count =E<gt> 2 }> 3.1, 3.0 + C<{ count =E<gt> -2 }> 1.3, 1.2 + C<{ count =E<gt> 3, offset=E<gt> 2 }> 2.2, 2.1, 2.0 + C<{ count =E<gt> 2, offset=E<gt> -3 }> 2.0, 1.3 + C<{ count =E<gt> -2, offset=E<gt> 3 }> 3.0, 2.2 + C<{ count =E<gt> -2, offset=E<gt> -3 }> 2.2, 2.1 + +Any combination of one option of C<since> and C<from> and one of +C<until> and C<to> returns the intersection of the two results +with only one of the options specified. + +=head1 AUTHOR + +Frank Lichtenheld, E<lt>frank@lichtenheld.deE<gt> +Raphaël Hertzog, E<lt>hertzog@debian.orgE<gt> + +=cut +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Changelog/Debian.pm b/third_party/dpkg-dev/scripts/Dpkg/Changelog/Debian.pm new file mode 100644 index 0000000..1643fb3 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Changelog/Debian.pm
@@ -0,0 +1,208 @@ +# Copyright © 1996 Ian Jackson +# Copyright © 2005 Frank Lichtenheld <frank@lichtenheld.de> +# Copyright © 2009 Raphaël Hertzog <hertzog@debian.org> +# Copyright © 2012-2013 Guillem Jover <guillem@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +=encoding utf8 + +=head1 NAME + +Dpkg::Changelog::Debian - parse Debian changelogs + +=head1 DESCRIPTION + +Dpkg::Changelog::Debian parses Debian changelogs as described in the Debian +policy (version 3.6.2.1 at the time of this writing). See section +L<"SEE ALSO"> for locations where to find this definition. + +The parser tries to ignore most cruft like # or /* */ style comments, +CVS comments, vim variables, emacs local variables and stuff from +older changelogs with other formats at the end of the file. +NOTE: most of these are ignored silently currently, there is no +parser error issued for them. This should become configurable in the +future. + +=head2 METHODS + +=cut + +package Dpkg::Changelog::Debian; + +use strict; +use warnings; + +our $VERSION = '1.00'; + +use Dpkg::Gettext; +use Dpkg::File; +use Dpkg::Changelog qw(:util); +use parent qw(Dpkg::Changelog); +use Dpkg::Changelog::Entry::Debian qw(match_header match_trailer); + +use constant { + FIRST_HEADING => _g('first heading'), + NEXT_OR_EOF => _g('next heading or eof'), + START_CHANGES => _g('start of change data'), + CHANGES_OR_TRAILER => _g('more change data or trailer'), +}; + +=over 4 + +=item $c->parse($fh, $description) + +Read the filehandle and parse a Debian changelog in it. Returns the number +of changelog entries that have been parsed with success. + +=cut + +sub parse { + my ($self, $fh, $file) = @_; + $file = $self->{reportfile} if exists $self->{reportfile}; + + $self->reset_parse_errors; + + $self->{data} = []; + $self->set_unparsed_tail(undef); + + my $expect = FIRST_HEADING; + my $entry = Dpkg::Changelog::Entry::Debian->new(); + my @blanklines = (); + my $unknowncounter = 1; # to make version unique, e.g. for using as id + + while (<$fh>) { + chomp; + if (match_header($_)) { + unless ($expect eq FIRST_HEADING || $expect eq NEXT_OR_EOF) { + $self->parse_error($file, $., + sprintf(_g('found start of entry where expected %s'), + $expect), "$_"); + } + unless ($entry->is_empty) { + push @{$self->{data}}, $entry; + $entry = Dpkg::Changelog::Entry::Debian->new(); + last if $self->abort_early(); + } + $entry->set_part('header', $_); + foreach my $error ($entry->check_header()) { + $self->parse_error($file, $., $error, $_); + } + $expect= START_CHANGES; + @blanklines = (); + } elsif (m/^(;;\s*)?Local variables:/io) { + last; # skip Emacs variables at end of file + } elsif (m/^vim:/io) { + last; # skip vim variables at end of file + } elsif (m/^\$\w+:.*\$/o) { + next; # skip stuff that look like a CVS keyword + } elsif (m/^\# /o) { + next; # skip comments, even that's not supported + } elsif (m{^/\*.*\*/}o) { + next; # more comments + } elsif (m/^(\w+\s+\w+\s+\d{1,2} \d{1,2}:\d{1,2}:\d{1,2}\s+[\w\s]*\d{4})\s+(.*)\s+(<|\()(.*)(\)|>)/o + || m/^(\w+\s+\w+\s+\d{1,2},?\s*\d{4})\s+(.*)\s+(<|\()(.*)(\)|>)/o + || m/^(\w[-+0-9a-z.]*) \(([^\(\) \t]+)\)\;?/io + || m/^([\w.+-]+)(-| )(\S+) Debian (\S+)/io + || m/^Changes from version (.*) to (.*):/io + || m/^Changes for [\w.+-]+-[\w.+-]+:?\s*$/io + || m/^Old Changelog:\s*$/io + || m/^(?:\d+:)?\w[\w.+~-]*:?\s*$/o) { + # save entries on old changelog format verbatim + # we assume the rest of the file will be in old format once we + # hit it for the first time + $self->set_unparsed_tail("$_\n" . file_slurp($fh)); + } elsif (m/^\S/) { + $self->parse_error($file, $., _g('badly formatted heading line'), "$_"); + } elsif (match_trailer($_)) { + unless ($expect eq CHANGES_OR_TRAILER) { + $self->parse_error($file, $., + sprintf(_g('found trailer where expected %s'), $expect), "$_"); + } + $entry->set_part('trailer', $_); + $entry->extend_part('blank_after_changes', [ @blanklines ]); + @blanklines = (); + foreach my $error ($entry->check_trailer()) { + $self->parse_error($file, $., $error, $_); + } + $expect = NEXT_OR_EOF; + } elsif (m/^ \-\-/) { + $self->parse_error($file, $., _g('badly formatted trailer line'), "$_"); + } elsif (m/^\s{2,}(\S)/) { + unless ($expect eq START_CHANGES or $expect eq CHANGES_OR_TRAILER) { + $self->parse_error($file, $., sprintf(_g('found change data' . + ' where expected %s'), $expect), "$_"); + if ($expect eq NEXT_OR_EOF and not $entry->is_empty) { + # lets assume we have missed the actual header line + push @{$self->{data}}, $entry; + $entry = Dpkg::Changelog::Entry::Debian->new(); + $entry->set_part('header', 'unknown (unknown' . ($unknowncounter++) . ') unknown; urgency=unknown'); + } + } + # Keep raw changes + $entry->extend_part('changes', [ @blanklines, $_ ]); + @blanklines = (); + $expect = CHANGES_OR_TRAILER; + } elsif (!m/\S/) { + if ($expect eq START_CHANGES) { + $entry->extend_part('blank_after_header', $_); + next; + } elsif ($expect eq NEXT_OR_EOF) { + $entry->extend_part('blank_after_trailer', $_); + next; + } elsif ($expect ne CHANGES_OR_TRAILER) { + $self->parse_error($file, $., + sprintf(_g('found blank line where expected %s'), $expect)); + } + push @blanklines, $_; + } else { + $self->parse_error($file, $., _g('unrecognized line'), "$_"); + unless ($expect eq START_CHANGES or $expect eq CHANGES_OR_TRAILER) { + # lets assume change data if we expected it + $entry->extend_part('changes', [ @blanklines, $_]); + @blanklines = (); + $expect = CHANGES_OR_TRAILER; + } + } + } + + unless ($expect eq NEXT_OR_EOF) { + $self->parse_error($file, $., sprintf(_g('found eof where expected %s'), + $expect)); + } + unless ($entry->is_empty) { + push @{$self->{data}}, $entry; + } + + return scalar @{$self->{data}}; +} + +1; +__END__ + +=back + +=head1 SEE ALSO + +Dpkg::Changelog + +Description of the Debian changelog format in the Debian policy: +L<http://www.debian.org/doc/debian-policy/ch-source.html#s-dpkgchangelog>. + +=head1 AUTHORS + +Frank Lichtenheld, E<lt>frank@lichtenheld.deE<gt> +Raphaël Hertzog, E<lt>hertzog@debian.orgE<gt> + +=cut
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Changelog/Entry.pm b/third_party/dpkg-dev/scripts/Dpkg/Changelog/Entry.pm new file mode 100644 index 0000000..3277ab6 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Changelog/Entry.pm
@@ -0,0 +1,306 @@ +# Copyright © 2009 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Changelog::Entry; + +use strict; +use warnings; + +our $VERSION = '1.00'; + +use Carp; + +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Control::Changelog; + +use overload + '""' => \&output, + 'eq' => sub { defined($_[1]) and "$_[0]" eq "$_[1]" }, + fallback => 1; + +=encoding utf8 + +=head1 NAME + +Dpkg::Changelog::Entry - represents a changelog entry + +=head1 DESCRIPTION + +This object represents a changelog entry. It is composed +of a set of lines with specific purpose: an header line, changes lines, a +trailer line. Blank lines can be between those kind of lines. + +=head1 FUNCTIONS + +=over 4 + +=item my $entry = Dpkg::Changelog::Entry->new() + +Creates a new object. It doesn't represent a real changelog entry +until one has been successfully parsed or built from scratch. + +=cut + +sub new { + my ($this) = @_; + my $class = ref($this) || $this; + + my $self = { + header => undef, + changes => [], + trailer => undef, + blank_after_header => [], + blank_after_changes => [], + blank_after_trailer => [], + }; + bless $self, $class; + return $self; +} + +=item my $str = $entry->output() + +=item "$entry" + +Get a string representation of the changelog entry. + +=item $entry->output($fh) + +Print the string representation of the changelog entry to a +filehandle. + +=cut + +sub _format_output_block { + my $lines = shift; + return join('', map { $_ . "\n" } @{$lines}); +} + +sub output { + my ($self, $fh) = @_; + my $str = ''; + $str .= $self->{header} . "\n" if defined($self->{header}); + $str .= _format_output_block($self->{blank_after_header}); + $str .= _format_output_block($self->{changes}); + $str .= _format_output_block($self->{blank_after_changes}); + $str .= $self->{trailer} . "\n" if defined($self->{trailer}); + $str .= _format_output_block($self->{blank_after_trailer}); + print { $fh } $str if defined $fh; + return $str; +} + +=item $entry->get_part($part) + +Return either a string (for a single line) or an array ref (for multiple +lines) corresponding to the requested part. $part can be +"header, "changes", "trailer", "blank_after_header", +"blank_after_changes", "blank_after_trailer". + +=cut + +sub get_part { + my ($self, $part) = @_; + croak "invalid part of changelog entry: $part" unless exists $self->{$part}; + return $self->{$part}; +} + +=item $entry->set_part($part, $value) + +Set the value of the corresponding part. $value can be a string +or an array ref. + +=cut + +sub set_part { + my ($self, $part, $value) = @_; + croak "invalid part of changelog entry: $part" unless exists $self->{$part}; + if (ref($self->{$part})) { + if (ref($value)) { + $self->{$part} = $value; + } else { + $self->{$part} = [ $value ]; + } + } else { + $self->{$part} = $value; + } +} + +=item $entry->extend_part($part, $value) + +Concatenate $value at the end of the part. If the part is already a +multi-line value, $value is added as a new line otherwise it's +concatenated at the end of the current line. + +=cut + +sub extend_part { + my ($self, $part, $value, @rest) = @_; + croak "invalid part of changelog entry: $part" unless exists $self->{$part}; + if (ref($self->{$part})) { + if (ref($value)) { + push @{$self->{$part}}, @$value; + } else { + push @{$self->{$part}}, $value; + } + } else { + if (defined($self->{$part})) { + if (ref($value)) { + $self->{$part} = [ $self->{$part}, @$value ]; + } else { + $self->{$part} .= $value; + } + } else { + $self->{$part} = $value; + } + } +} + +=item $is_empty = $entry->is_empty() + +Returns 1 if the changelog entry doesn't contain anything at all. +Returns 0 as soon as it contains something in any of its non-blank +parts. + +=cut + +sub is_empty { + my ($self) = @_; + return !(defined($self->{header}) || defined($self->{trailer}) || + scalar(@{$self->{changes}})); +} + +=item $entry->normalize() + +Normalize the content. Strip whitespaces at end of lines, use a single +empty line to separate each part. + +=cut + +sub normalize { + my ($self) = @_; + if (defined($self->{header})) { + $self->{header} =~ s/\s+$//g; + $self->{blank_after_header} = ['']; + } else { + $self->{blank_after_header} = []; + } + if (scalar(@{$self->{changes}})) { + s/\s+$//g foreach @{$self->{changes}}; + $self->{blank_after_changes} = ['']; + } else { + $self->{blank_after_changes} = []; + } + if (defined($self->{trailer})) { + $self->{trailer} =~ s/\s+$//g; + $self->{blank_after_trailer} = ['']; + } else { + $self->{blank_after_trailer} = []; + } +} + +=item my $src = $entry->get_source() + +Return the name of the source package associated to the changelog entry. + +=cut + +sub get_source { + return; +} + +=item my $ver = $entry->get_version() + +Return the version associated to the changelog entry. + +=cut + +sub get_version { + return; +} + +=item my @dists = $entry->get_distributions() + +Return a list of target distributions for this version. + +=cut + +sub get_distributions { + return; +} + +=item $fields = $entry->get_optional_fields() + +Return a set of optional fields exposed by the changelog entry. +It always returns a Dpkg::Control object (possibly empty though). + +=cut + +sub get_optional_fields { + return Dpkg::Control::Changelog->new(); +} + +=item $urgency = $entry->get_urgency() + +Return the urgency of the associated upload. + +=cut + +sub get_urgency { + return; +} + +=item my $maint = $entry->get_maintainer() + +Return the string identifying the person who signed this changelog entry. + +=cut + +sub get_maintainer { + return; +} + +=item my $time = $entry->get_timestamp() + +Return the timestamp of the changelog entry. + +=cut + +sub get_timestamp { + return; +} + +=item my $str = $entry->get_dpkg_changes() + +Returns a string that is suitable for usage in a C<Changes> field +in the output format of C<dpkg-parsechangelog>. + +=cut + +sub get_dpkg_changes { + my ($self) = @_; + my $header = $self->get_part('header') || ''; + $header =~ s/\s+$//; + return "\n$header\n\n" . join("\n", @{$self->get_part('changes')}); +} + +=back + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Changelog/Entry/Debian.pm b/third_party/dpkg-dev/scripts/Dpkg/Changelog/Entry/Debian.pm new file mode 100644 index 0000000..8d05953 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Changelog/Entry/Debian.pm
@@ -0,0 +1,323 @@ +# Copyright © 2009 Raphaël Hertzog <hertzog@debian.org> +# Copyright © 2012-2013 Guillem Jover <guillem@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Changelog::Entry::Debian; + +use strict; +use warnings; + +our $VERSION = '1.01'; + +use Exporter qw(import); +use Dpkg::Changelog::Entry; +use parent qw(Dpkg::Changelog::Entry); +our @EXPORT_OK = qw(match_header match_trailer find_closes + $regex_header $regex_trailer); + +use Date::Parse; + +use Dpkg::Gettext; +use Dpkg::Control::Fields; +use Dpkg::Control::Changelog; +use Dpkg::Version; + +=encoding utf8 + +=head1 NAME + +Dpkg::Changelog::Entry::Debian - represents a Debian changelog entry + +=head1 DESCRIPTION + +This object represents a Debian changelog entry. It implements the +generic interface Dpkg::Changelog::Entry. Only functions specific to this +implementation are described below. + +=cut + +my $name_chars = qr/[-+0-9a-z.]/i; + +# XXX: Backwards compatibility, stop exporting on VERSION 2.00. +## no critic (Variables::ProhibitPackageVars) + +# The matched content is the source package name ($1), the version ($2), +# the target distributions ($3) and the options on the rest of the line ($4). +our $regex_header = qr/^(\w$name_chars*) \(([^\(\) \t]+)\)((?:\s+$name_chars+)+)\;(.*?)\s*$/i; + +# The matched content is the maintainer name ($1), its email ($2), +# some blanks ($3) and the timestamp ($4). +our $regex_trailer = qr/^ \-\- (.*) <(.*)>( ?)((\w+\,\s*)?\d{1,2}\s+\w+\s+\d{4}\s+\d{1,2}:\d\d:\d\d\s+[-+]\d{4}(\s+\([^\\\(\)]\))?)\s*$/o; + +## use critic + +=head1 FUNCTIONS + +=over 4 + +=item my @items = $entry->get_change_items() + +Return a list of change items. Each item contains at least one line. +A change line starting with an asterisk denotes the start of a new item. +Any change line like "[ Raphaël Hertzog ]" is treated like an item of its +own even if it starts a set of items attributed to this person (the +following line necessarily starts a new item). + +=cut + +sub get_change_items { + my ($self) = @_; + my (@items, @blanks, $item); + foreach my $line (@{$self->get_part('changes')}) { + if ($line =~ /^\s*\*/) { + push @items, $item if defined $item; + $item = "$line\n"; + } elsif ($line =~ /^\s*\[\s[^\]]+\s\]\s*$/) { + push @items, $item if defined $item; + push @items, "$line\n"; + $item = undef; + @blanks = (); + } elsif ($line =~ /^\s*$/) { + push @blanks, "$line\n"; + } else { + if (defined $item) { + $item .= "@blanks$line\n"; + } else { + $item = "$line\n"; + } + @blanks = (); + } + } + push @items, $item if defined $item; + return @items; +} + +=item my @errors = $entry->check_header() + +=item my @errors = $entry->check_trailer() + +Return a list of errors. Each item in the list is an error message +describing the problem. If the empty list is returned, no errors +have been found. + +=cut + +sub check_header { + my ($self) = @_; + my @errors; + if (defined($self->{header}) and $self->{header} =~ $regex_header) { + my ($version, $options) = ($2, $4); + $options =~ s/^\s+//; + my %optdone; + foreach my $opt (split(/\s*,\s*/, $options)) { + unless ($opt =~ m/^([-0-9a-z]+)\=\s*(.*\S)$/i) { + push @errors, sprintf(_g("bad key-value after \`;': \`%s'"), $opt); + next; + } + my ($k, $v) = (field_capitalize($1), $2); + if ($optdone{$k}) { + push @errors, sprintf(_g('repeated key-value %s'), $k); + } + $optdone{$k} = 1; + if ($k eq 'Urgency') { + push @errors, sprintf(_g('badly formatted urgency value: %s'), $v) + unless ($v =~ m/^([-0-9a-z]+)((\s+.*)?)$/i); + } elsif ($k eq 'Binary-Only') { + push @errors, sprintf(_g('bad binary-only value: %s'), $v) + unless ($v eq 'yes'); + } elsif ($k =~ m/^X[BCS]+-/i) { + } else { + push @errors, sprintf(_g('unknown key-value %s'), $k); + } + } + my ($ok, $msg) = version_check($version); + unless ($ok) { + push @errors, sprintf(_g("version '%s' is invalid: %s"), $version, $msg); + } + } else { + push @errors, _g("the header doesn't match the expected regex"); + } + return @errors; +} + +sub check_trailer { + my ($self) = @_; + my @errors; + if (defined($self->{trailer}) and $self->{trailer} =~ $regex_trailer) { + if ($3 ne ' ') { + push @errors, _g('badly formatted trailer line'); + } + unless (defined str2time($4)) { + push @errors, sprintf(_g("couldn't parse date %s"), $4); + } + } else { + push @errors, _g("the trailer doesn't match the expected regex"); + } + return @errors; +} + +=item $entry->normalize() + +Normalize the content. Strip whitespaces at end of lines, use a single +empty line to separate each part. + +=cut + +sub normalize { + my ($self) = @_; + $self->SUPER::normalize(); + #XXX: recreate header/trailer +} + +sub get_source { + my ($self) = @_; + if (defined($self->{header}) and $self->{header} =~ $regex_header) { + return $1; + } + return; +} + +sub get_version { + my ($self) = @_; + if (defined($self->{header}) and $self->{header} =~ $regex_header) { + return Dpkg::Version->new($2); + } + return; +} + +sub get_distributions { + my ($self) = @_; + if (defined($self->{header}) and $self->{header} =~ $regex_header) { + my $value = $3; + $value =~ s/^\s+//; + my @dists = split(/\s+/, $value); + return @dists if wantarray; + return $dists[0]; + } + return; +} + +sub get_optional_fields { + my ($self) = @_; + my $f = Dpkg::Control::Changelog->new(); + if (defined($self->{header}) and $self->{header} =~ $regex_header) { + my $options = $4; + $options =~ s/^\s+//; + foreach my $opt (split(/\s*,\s*/, $options)) { + if ($opt =~ m/^([-0-9a-z]+)\=\s*(.*\S)$/i) { + $f->{$1} = $2; + } + } + } + my @closes = find_closes(join("\n", @{$self->{changes}})); + if (@closes) { + $f->{Closes} = join(' ', @closes); + } + return $f; +} + +sub get_urgency { + my ($self) = @_; + my $f = $self->get_optional_fields(); + if (exists $f->{Urgency}) { + $f->{Urgency} =~ s/\s.*$//; + return lc($f->{Urgency}); + } + return; +} + +sub get_maintainer { + my ($self) = @_; + if (defined($self->{trailer}) and $self->{trailer} =~ $regex_trailer) { + return "$1 <$2>"; + } + return; +} + +sub get_timestamp { + my ($self) = @_; + if (defined($self->{trailer}) and $self->{trailer} =~ $regex_trailer) { + return $4; + } + return; +} + +=back + +=head1 UTILITY FUNCTIONS + +=over 4 + +=item my $bool = match_header($line) + +Checks if the line matches a valid changelog header line. + +=cut + +sub match_header { + my ($line) = @_; + + return $line =~ /$regex_header/; +} + +=item my $bool = match_trailer($line) + +Checks if the line matches a valid changelog trailing line. + +=cut + +sub match_trailer { + my ($line) = @_; + + return $line =~ /$regex_trailer/; +} + +=item my @closed_bugs = find_closes($changes) + +Takes one string as argument and finds "Closes: #123456, #654321" statements +as supported by the Debian Archive software in it. Returns all closed bug +numbers in an array. + +=cut + +sub find_closes { + my $changes = shift; + my %closes; + + while ($changes && + ($changes =~ /closes:\s*(?:bug)?\#?\s?\d+(?:,\s*(?:bug)?\#?\s?\d+)*/ig)) { + $closes{$_} = 1 foreach($& =~ /\#?\s?(\d+)/g); + } + + my @closes = sort { $a <=> $b } keys %closes; + return @closes; +} + +=back + +=head1 CHANGES + +=head2 Version 1.01 + +New functions: match_header(), match_trailer() +Deprecated variables: $regex_header, $regex_trailer + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Changelog/Parse.pm b/third_party/dpkg-dev/scripts/Dpkg/Changelog/Parse.pm new file mode 100644 index 0000000..41c4440d --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Changelog/Parse.pm
@@ -0,0 +1,167 @@ +# Copyright © 2005, 2007 Frank Lichtenheld <frank@lichtenheld.de> +# Copyright © 2009 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +=encoding utf8 + +=head1 NAME + +Dpkg::Changelog::Parse - generic changelog parser for dpkg-parsechangelog + +=head1 DESCRIPTION + +This module provides a single function changelog_parse() which reproduces +all the features of dpkg-parsechangelog. + +=head2 FUNCTIONS + +=cut + +package Dpkg::Changelog::Parse; + +use strict; +use warnings; + +our $VERSION = '1.00'; + +use Dpkg (); +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Control::Changelog; + +use Exporter qw(import); +our @EXPORT = qw(changelog_parse); + +=over 4 + +=item my $fields = changelog_parse(%opt) + +This function will parse a changelog. In list context, it return as many +Dpkg::Control object as the parser did output. In scalar context, it will +return only the first one. If the parser didn't return any data, it will +return an empty in list context or undef on scalar context. If the parser +failed, it will die. + +The parsing itself is done by an external program (searched in the +following list of directories: $opt{libdir}, +F</usr/local/lib/dpkg/parsechangelog>, F</usr/lib/dpkg/parsechangelog>) That +program is named according to the format that it's able to parse. By +default it's either "debian" or the format name lookep up in the 40 last +lines of the changelog itself (extracted with this perl regular expression +"\schangelog-format:\s+([0-9a-z]+)\W"). But it can be overridden +with $opt{changelogformat}. The program expects the content of the +changelog file on its standard input. + +The changelog file that is parsed is F<debian/changelog> by default but it +can be overridden with $opt{file}. + +All the other keys in %opt are forwarded as parameter to the external +parser. If the key starts with "-", it's passed as is. If not, it's passed +as "--<key>". If the value of the corresponding hash entry is defined, then +it's passed as the parameter that follows. + +=cut + +sub changelog_parse { + my (%options) = @_; + my @parserpath = ('/usr/local/lib/dpkg/parsechangelog', + "$Dpkg::LIBDIR/parsechangelog", + '/usr/lib/dpkg/parsechangelog'); + my $format = 'debian'; + my $force = 0; + + # Extract and remove options that do not concern the changelog parser + # itself (and that we shouldn't forward) + if (exists $options{libdir}) { + unshift @parserpath, $options{libdir}; + delete $options{libdir}; + } + if (exists $options{changelogformat}) { + $format = $options{changelogformat}; + delete $options{changelogformat}; + $force = 1; + } + + # Set a default filename + if (not exists $options{file}) { + $options{file} = 'debian/changelog'; + } + my $changelogfile = $options{file}; + + # Extract the format from the changelog file if possible + unless($force or ($changelogfile eq '-')) { + open(my $format_fh, '-|', 'tail', '-n', '40', $changelogfile) + or syserr(_g('cannot create pipe for %s'), 'tail'); + while (<$format_fh>) { + $format = $1 if m/\schangelog-format:\s+([0-9a-z]+)\W/; + } + close($format_fh) or subprocerr(_g('tail of %s'), $changelogfile); + } + + # Find the right changelog parser + my $parser; + foreach my $dir (@parserpath) { + my $candidate = "$dir/$format"; + next if not -e $candidate; + if (-x _) { + $parser = $candidate; + last; + } else { + warning(_g('format parser %s not executable'), $candidate); + } + } + error(_g('changelog format %s is unknown'), $format) if not defined $parser; + + # Create the arguments for the changelog parser + my @exec = ($parser, "-l$changelogfile"); + foreach (keys %options) { + if (m/^-/) { + # Options passed untouched + push @exec, $_; + } else { + # Non-options are mapped to long options + push @exec, "--$_"; + } + push @exec, $options{$_} if defined($options{$_}); + } + + # Fork and call the parser + my $pid = open(my $parser_fh, '-|'); + syserr(_g('cannot fork for %s'), $parser) unless defined $pid; + if (not $pid) { + exec(@exec) or syserr(_g('cannot exec format parser: %s'), $parser); + } + + # Get the output into several Dpkg::Control objects + my (@res, $fields); + while (1) { + $fields = Dpkg::Control::Changelog->new(); + last unless $fields->parse($parser_fh, _g('output of changelog parser')); + push @res, $fields; + } + close($parser_fh) or subprocerr(_g('changelog parser %s'), $parser); + if (wantarray) { + return @res; + } else { + return $res[0] if (@res); + return; + } +} + +=back + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Checksums.pm b/third_party/dpkg-dev/scripts/Dpkg/Checksums.pm new file mode 100644 index 0000000..5b6d0ab --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Checksums.pm
@@ -0,0 +1,378 @@ +# Copyright © 2008 Frank Lichtenheld <djpig@debian.org> +# Copyright © 2010 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Checksums; + +use strict; +use warnings; + +our $VERSION = '1.00'; + +use Dpkg; +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::IPC; + +use Exporter qw(import); +our @EXPORT = qw(checksums_get_list checksums_is_supported + checksums_get_property); + +=encoding utf8 + +=head1 NAME + +Dpkg::Checksums - generate and manipulate file checksums + +=head1 DESCRIPTION + +This module provides an object that can generate and manipulate +various file checksums as well as some methods to query information +about supported checksums. + +=head1 EXPORTED FUNCTIONS + +=over 4 + +=cut + +my $CHECKSUMS = { + md5 => { + program => [ 'md5sum' ], + regex => qr/[0-9a-f]{32}/, + }, + sha1 => { + program => [ 'sha1sum' ], + regex => qr/[0-9a-f]{40}/, + }, + sha256 => { + program => [ 'sha256sum' ], + regex => qr/[0-9a-f]{64}/, + }, +}; + +=item @list = checksums_get_list() + +Returns the list of supported checksums algorithms. + +=cut + +sub checksums_get_list() { + my @list = sort keys %{$CHECKSUMS}; + return @list; +} + +=item $bool = checksums_is_supported($alg) + +Returns a boolean indicating whether the given checksum algorithm is +supported. The checksum algorithm is case-insensitive. + +=cut + +sub checksums_is_supported($) { + my ($alg) = @_; + return exists $CHECKSUMS->{lc($alg)}; +} + +=item $value = checksums_get_property($alg, $property) + +Returns the requested property of the checksum algorithm. Returns undef if +either the property or the checksum algorithm doesn't exist. Valid +properties currently include "program" (returns an array reference with +a program name and parameters required to compute the checksum of the +filename given as last parameter) and "regex" for the regular expression +describing the common string representation of the checksum (as output +by the program that generates it). + +=cut + +sub checksums_get_property($$) { + my ($alg, $property) = @_; + return unless checksums_is_supported($alg); + return $CHECKSUMS->{lc($alg)}{$property}; +} + +=back + +=head1 OBJECT METHODS + +=over 4 + +=item my $ck = Dpkg::Checksums->new() + +Create a new Dpkg::Checksums object. This object is able to store +the checksums of several files to later export them or verify them. + +=cut + +sub new { + my ($this, %opts) = @_; + my $class = ref($this) || $this; + + my $self = {}; + bless $self, $class; + $self->reset(); + + return $self; +} + +=item $ck->reset() + +Forget about all checksums stored. The object is again in the same state +as if it was newly created. + +=cut + +sub reset { + my ($self) = @_; + $self->{files} = []; + $self->{checksums} = {}; + $self->{size} = {}; +} + +=item $ck->add_from_file($filename, %opts) + +Add checksums information for the file $filename. The file must exists +for the call to succeed. If you don't want the given filename to appear +when you later export the checksums you might want to set the "key" +option with the public name that you want to use. Also if you don't want +to generate all the checksums, you can pass an array reference of the +wanted checksums in the "checksums" option. + +It the object already contains checksums information associated the +filename (or key), it will error out if the newly computed information +does not match what's stored. + +=cut + +sub add_from_file { + my ($self, $file, %opts) = @_; + my $key = exists $opts{key} ? $opts{key} : $file; + my @alg; + if (exists $opts{checksums}) { + push @alg, map { lc($_) } @{$opts{checksums}}; + } else { + push @alg, checksums_get_list(); + } + + push @{$self->{files}}, $key unless exists $self->{size}{$key}; + (my @s = stat($file)) or syserr(_g('cannot fstat file %s'), $file); + if (exists $self->{size}{$key} and $self->{size}{$key} != $s[7]) { + error(_g('file %s has size %u instead of expected %u'), + $file, $s[7], $self->{size}{$key}); + } + $self->{size}{$key} = $s[7]; + + foreach my $alg (@alg) { + my @exec = (@{$CHECKSUMS->{$alg}{program}}, $file); + my $regex = $CHECKSUMS->{$alg}{regex}; + my $output; + spawn(exec => \@exec, to_string => \$output); + if ($output =~ /^($regex)(\s|$)/m) { + my $newsum = $1; + if (exists $self->{checksums}{$key}{$alg} and + $self->{checksums}{$key}{$alg} ne $newsum) { + error(_g('file %s has checksum %s instead of expected %s (algorithm %s)'), + $file, $newsum, $self->{checksums}{$key}{$alg}, $alg); + } + $self->{checksums}{$key}{$alg} = $newsum; + } else { + error(_g("checksum program gave bogus output `%s'"), $output); + } + } +} + +=item $ck->add_from_string($alg, $value) + +Add checksums of type $alg that are stored in the $value variable. +$value can be multi-lines, each line should be a space separated list +of checksum, file size and filename. Leading or trailing spaces are +not allowed. + +It the object already contains checksums information associated to the +filenames, it will error out if the newly read information does not match +what's stored. + +=cut + +sub add_from_string { + my ($self, $alg, $fieldtext) = @_; + $alg = lc($alg); + my $rx_fname = qr/[0-9a-zA-Z][-+:.,=0-9a-zA-Z_~]+/; + my $regex = checksums_get_property($alg, 'regex'); + my $checksums = $self->{checksums}; + + for my $checksum (split /\n */, $fieldtext) { + next if $checksum eq ''; + unless ($checksum =~ m/^($regex)\s+(\d+)\s+($rx_fname)$/) { + error(_g('invalid line in %s checksums string: %s'), + $alg, $checksum); + } + my ($sum, $size, $file) = ($1, $2, $3); + if (exists($checksums->{$file}{$alg}) + and $checksums->{$file}{$alg} ne $sum) { + error(_g("conflicting checksums '%s' and '%s' for file '%s'"), + $checksums->{$file}{$alg}, $sum, $file); + } + if (exists $self->{size}{$file} and $self->{size}{$file} != $size) { + error(_g("conflicting file sizes '%u' and '%u' for file '%s'"), + $self->{size}{$file}, $size, $file); + } + push @{$self->{files}}, $file unless exists $self->{size}{$file}; + $checksums->{$file}{$alg} = $sum; + $self->{size}{$file} = $size; + } +} + +=item $ck->add_from_control($control, %opts) + +Read checksums from Checksums-* fields stored in the Dpkg::Control object +$control. It uses $self->add_from_string() on the field values to do the +actual work. + +If the option "use_files_for_md5" evaluates to true, then the "Files" +field is used in place of the "Checksums-Md5" field. By default the option +is false. + +=cut + +sub add_from_control { + my ($self, $control, %opts) = @_; + $opts{use_files_for_md5} = 0 unless exists $opts{use_files_for_md5}; + foreach my $alg (checksums_get_list()) { + my $key = "Checksums-$alg"; + $key = 'Files' if ($opts{use_files_for_md5} and $alg eq 'md5'); + if (exists $control->{$key}) { + $self->add_from_string($alg, $control->{$key}); + } + } +} + +=item @files = $ck->get_files() + +Return the list of files whose checksums are stored in the object. + +=cut + +sub get_files { + my ($self) = @_; + return @{$self->{files}}; +} + +=item $bool = $ck->has_file($file) + +Return true if we have checksums for the given file. Returns false +otherwise. + +=cut + +sub has_file { + my ($self, $file) = @_; + return exists $self->{size}{$file}; +} + +=item $ck->remove_file($file) + +Remove all checksums of the given file. + +=cut + +sub remove_file { + my ($self, $file) = @_; + return unless $self->has_file($file); + delete $self->{checksums}{$file}; + delete $self->{size}{$file}; + @{$self->{files}} = grep { $_ ne $file } $self->get_files(); +} + +=item $checksum = $ck->get_checksum($file, $alg) + +Return the checksum of type $alg for the requested $file. This will not +compute the checksum but only return the checksum stored in the object, if +any. + +If $alg is not defined, it returns a reference to a hash: keys are +the checksum algorithms and values are the checksums themselves. The +hash returned must not be modified, it's internal to the object. + +=cut + +sub get_checksum { + my ($self, $file, $alg) = @_; + $alg = lc($alg) if defined $alg; + if (exists $self->{checksums}{$file}) { + return $self->{checksums}{$file} unless defined $alg; + return $self->{checksums}{$file}{$alg}; + } + return; +} + +=item $size = $ck->get_size($file) + +Return the size of the requested file if it's available in the object. + +=cut + +sub get_size { + my ($self, $file) = @_; + return $self->{size}{$file}; +} + +=item $ck->export_to_string($alg, %opts) + +Return a multi-line string containing the checksums of type $alg. The +string can be stored as-is in a Checksum-* field of a Dpkg::Control +object. + +=cut + +sub export_to_string { + my ($self, $alg, %opts) = @_; + my $res = ''; + foreach my $file ($self->get_files()) { + my $sum = $self->get_checksum($file, $alg); + my $size = $self->get_size($file); + next unless defined $sum and defined $size; + $res .= "\n$sum $size $file"; + } + return $res; +} + +=item $ck->export_to_control($control, %opts) + +Export the checksums in the Checksums-* fields of the Dpkg::Control +$control object. + +=cut + +sub export_to_control { + my ($self, $control, %opts) = @_; + $opts{use_files_for_md5} = 0 unless exists $opts{use_files_for_md5}; + foreach my $alg (checksums_get_list()) { + my $key = "Checksums-$alg"; + $key = 'Files' if ($opts{use_files_for_md5} and $alg eq 'md5'); + $control->{$key} = $self->export_to_string($alg, %opts); + } +} + +=back + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Compression.pm b/third_party/dpkg-dev/scripts/Dpkg/Compression.pm new file mode 100644 index 0000000..3fe638d --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Compression.pm
@@ -0,0 +1,244 @@ +# Copyright © 2010 Raphaël Hertzog <hertzog@debian.org> +# Copyright © 2010-2013 Guillem Jover <guillem@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Compression; + +use strict; +use warnings; + +our $VERSION = '1.02'; + +use Dpkg::ErrorHandling; +use Dpkg::Gettext; + +use Exporter qw(import); +our @EXPORT = qw($compression_re_file_ext compression_get_list + compression_is_supported compression_get_property + compression_guess_from_filename + compression_get_file_extension_regex + compression_get_default compression_set_default + compression_get_default_level + compression_set_default_level + compression_is_valid_level); + +=encoding utf8 + +=head1 NAME + +Dpkg::Compression - simple database of available compression methods + +=head1 DESCRIPTION + +This modules provides a few public funcions and a public regex to +interact with the set of supported compression methods. + +=cut + +my $COMP = { + gzip => { + file_ext => 'gz', + comp_prog => [ 'gzip', '--no-name', '--rsyncable' ], + decomp_prog => [ 'gunzip' ], + default_level => 9, + }, + bzip2 => { + file_ext => 'bz2', + comp_prog => [ 'bzip2' ], + decomp_prog => [ 'bunzip2' ], + default_level => 9, + }, + lzma => { + file_ext => 'lzma', + comp_prog => [ 'xz', '--format=lzma' ], + decomp_prog => [ 'unxz', '--format=lzma' ], + default_level => 6, + }, + xz => { + file_ext => 'xz', + comp_prog => [ 'xz' ], + decomp_prog => [ 'unxz' ], + default_level => 6, + }, +}; + +# XXX: Backwards compatibility, stop exporting on VERSION 2.00. +## no critic (Variables::ProhibitPackageVars) +our $default_compression = 'gzip'; +our $default_compression_level = undef; + +my $regex = join '|', map { $_->{file_ext} } values %$COMP; +our $compression_re_file_ext = qr/(?:$regex)/; +## use critic + +=head1 EXPORTED FUNCTIONS + +=over 4 + +=item my @list = compression_get_list() + +Returns a list of supported compression methods (sorted alphabetically). + +=cut + +sub compression_get_list { + my @list = sort keys %$COMP; + return @list; +} + +=item compression_is_supported($comp) + +Returns a boolean indicating whether the give compression method is +known and supported. + +=cut + +sub compression_is_supported { + return exists $COMP->{$_[0]}; +} + +=item compression_get_property($comp, $property) + +Returns the requested property of the compression method. Returns undef if +either the property or the compression method doesn't exist. Valid +properties currently include "file_ext" for the file extension, +"default_level" for the default compression level, +"comp_prog" for the name of the compression program and "decomp_prog" for +the name of the decompression program. + +=cut + +sub compression_get_property { + my ($comp, $property) = @_; + return unless compression_is_supported($comp); + return $COMP->{$comp}{$property} if exists $COMP->{$comp}{$property}; + return; +} + +=item compression_guess_from_filename($filename) + +Returns the compression method that is likely used on the indicated +filename based on its file extension. + +=cut + +sub compression_guess_from_filename { + my $filename = shift; + foreach my $comp (compression_get_list()) { + my $ext = compression_get_property($comp, 'file_ext'); + if ($filename =~ /^(.*)\.\Q$ext\E$/) { + return $comp; + } + } + return; +} + +=item my $regex = compression_get_file_extension_regex() + +Returns a regex that matches a file extension of a file compressed with +one of the supported compression methods. + +=cut + +sub compression_get_file_extension_regex { + return $compression_re_file_ext; +} + +=item my $comp = compression_get_default() + +Return the default compression method. It's "gzip" unless +C<compression_set_default> has been used to change it. + +=item compression_set_default($comp) + +Change the default compression method. Errors out if the +given compression method is not supported. + +=cut + +sub compression_get_default { + return $default_compression; +} + +sub compression_set_default { + my ($method) = @_; + error(_g('%s is not a supported compression'), $method) + unless compression_is_supported($method); + $default_compression = $method; +} + +=item my $level = compression_get_default_level() + +Return the default compression level used when compressing data. It's "9" +for "gzip" and "bzip2", "6" for "xz" and "lzma", unless +C<compression_set_default_level> has been used to change it. + +=item compression_set_default_level($level) + +Change the default compression level. Passing undef as the level will +reset it to the compressor specific default, otherwise errors out if the +level is not valid (see C<compression_is_valid_level>). + +=cut + +sub compression_get_default_level { + if (defined $default_compression_level) { + return $default_compression_level; + } else { + return compression_get_property($default_compression, 'default_level'); + } +} + +sub compression_set_default_level { + my ($level) = @_; + error(_g('%s is not a compression level'), $level) + if defined($level) and not compression_is_valid_level($level); + $default_compression_level = $level; +} + +=item compression_is_valid_level($level) + +Returns a boolean indicating whether $level is a valid compression level +(it must be either a number between 1 and 9 or "fast" or "best") + +=cut + +sub compression_is_valid_level { + my ($level) = @_; + return $level =~ /^([1-9]|fast|best)$/; +} + +=back + +=head1 CHANGES + +=head2 Version 1.02 + +New function: compression_get_file_extension_regex() + +Deprecated variables: $default_compression, $default_compression_level +and $compression_re_file_ext + +=head2 Version 1.01 + +Default compression level is not global any more, it is per compressor type. + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Compression/FileHandle.pm b/third_party/dpkg-dev/scripts/Dpkg/Compression/FileHandle.pm new file mode 100644 index 0000000..a214ea4a --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Compression/FileHandle.pm
@@ -0,0 +1,451 @@ +# Copyright © 2008-2010 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Compression::FileHandle; + +use strict; +use warnings; + +our $VERSION = '1.00'; + +use Dpkg::Compression; +use Dpkg::Compression::Process; +use Dpkg::Gettext; +use Dpkg::ErrorHandling; + +use Carp; +use POSIX qw(:signal_h :sys_wait_h); + +use parent qw(FileHandle Tie::Handle); + +# Useful reference to understand some kludges required to +# have the object behave like a filehandle +# http://blog.woobling.org/2009/10/are-filehandles-objects.html + +=encoding utf8 + +=head1 NAME + +Dpkg::Compression::FileHandle - object dealing transparently with file compression + +=head1 SYNOPSIS + + use Dpkg::Compression::FileHandle; + + $fh = Dpkg::Compression::FileHandle->new(filename => 'sample.gz'); + print $fh "Something\n"; + close $fh; + + $fh = Dpkg::Compression::FileHandle->new(); + open($fh, '>', 'sample.bz2'); + print $fh "Something\n"; + close $fh; + + $fh = Dpkg::Compression::FileHandle->new(); + $fh->open('sample.xz', 'w'); + $fh->print("Something\n"); + $fh->close(); + + $fh = Dpkg::Compression::FileHandle->new(filename => 'sample.gz'); + my @lines = <$fh>; + close $fh; + + $fh = Dpkg::Compression::FileHandle->new(); + open($fh, '<', 'sample.bz2'); + my @lines = <$fh>; + close $fh; + + $fh = Dpkg::Compression::FileHandle->new(); + $fh->open('sample.xz', 'r'); + my @lines = $fh->getlines(); + $fh->close(); + +=head1 DESCRIPTION + +Dpkg::Compression::FileHandle is an object that can be used +like any filehandle and that deals transparently with compressed +files. By default, the compression scheme is guessed from the filename +but you can override this behaviour with the method C<set_compression>. + +If you don't open the file explicitly, it will be auto-opened on the +first read or write operation based on the filename set at creation time +(or later with the C<set_filename> method). + +Once a file has been opened, the filehandle must be closed before being +able to open another file. + +=head1 STANDARD FUNCTIONS + +The standard functions acting on filehandles should accept a +Dpkg::Compression::FileHandle object transparently including +C<open> (only when using the variant with 3 parameters), C<close>, +C<binmode>, C<eof>, C<fileno>, C<getc>, C<print>, C<printf>, C<read>, +C<sysread>, C<say>, C<write>, C<syswrite>, C<seek>, C<sysseek>, C<tell>. + +Note however that C<seek> and C<sysseek> will only work on uncompressed +files as compressed files are really pipes to the compressor programs +and you can't seek on a pipe. + +=head1 FileHandle METHODS + +The object inherits from FileHandle so all methods that work on this +object should work for Dpkg::Compression::FileHandle too. There +may be exceptions though. + +=head1 PUBLIC METHODS + +=over 4 + +=item my $fh = Dpkg::Compression::FileHandle->new(%opts) + +Creates a new filehandle supporting on-the-fly compression/decompression. +Supported options are "filename", "compression", "compression_level" (see +respective set_* functions) and "add_comp_ext". If "add_comp_ext" +evaluates to true, then the extension corresponding to the selected +compression scheme is automatically added to the recorded filename. It's +obviously incompatible with automatic detection of the compression method. + +=cut + +# Object methods +sub new { + my ($this, %args) = @_; + my $class = ref($this) || $this; + my $self = FileHandle->new(); + # Tying is required to overload the open functions and to auto-open + # the file on first read/write operation + tie *$self, $class, $self; + bless $self, $class; + # Initializations + *$self->{compression} = 'auto'; + *$self->{compressor} = Dpkg::Compression::Process->new(); + *$self->{add_comp_ext} = $args{add_compression_extension} || + $args{add_comp_ext} || 0; + *$self->{allow_sigpipe} = 0; + if (exists $args{filename}) { + $self->set_filename($args{filename}); + } + if (exists $args{compression}) { + $self->set_compression($args{compression}); + } + if (exists $args{compression_level}) { + $self->set_compression_level($args{compression_level}); + } + return $self; +} + +=item $fh->ensure_open($mode) + +Ensure the file is opened in the requested mode ("r" for read and "w" for +write). Opens the file with the recorded filename if needed. If the file +is already open but not in the requested mode, then it errors out. + +=cut + +sub ensure_open { + my ($self, $mode) = @_; + if (exists *$self->{mode}) { + return if *$self->{mode} eq $mode; + croak "ensure_open requested incompatible mode: $mode"; + } else { + if ($mode eq 'w') { + $self->open_for_write(); + } elsif ($mode eq 'r') { + $self->open_for_read(); + } else { + croak "invalid mode in ensure_open: $mode"; + } + } +} + +## +## METHODS FOR TIED HANDLE +## +sub TIEHANDLE { + my ($class, $self) = @_; + return $self; +} + +sub WRITE { + my ($self, $scalar, $length, $offset) = @_; + $self->ensure_open('w'); + return *$self->{file}->write($scalar, $length, $offset); +} + +sub READ { + my ($self, $scalar, $length, $offset) = @_; + $self->ensure_open('r'); + return *$self->{file}->read($scalar, $length, $offset); +} + +sub READLINE { + my ($self) = shift; + $self->ensure_open('r'); + return *$self->{file}->getlines() if wantarray; + return *$self->{file}->getline(); +} + +sub OPEN { + my ($self) = shift; + if (scalar(@_) == 2) { + my ($mode, $filename) = @_; + $self->set_filename($filename); + if ($mode eq '>') { + $self->open_for_write(); + } elsif ($mode eq '<') { + $self->open_for_read(); + } else { + croak 'Dpkg::Compression::FileHandle does not support ' . + "open() mode $mode"; + } + } else { + croak 'Dpkg::Compression::FileHandle only supports open() ' . + 'with 3 parameters'; + } + return 1; # Always works (otherwise errors out) +} + +sub CLOSE { + my ($self) = shift; + my $ret = 1; + if (defined *$self->{file}) { + $ret = *$self->{file}->close(@_) if *$self->{file}->opened(); + } else { + $ret = 0; + } + $self->cleanup(); + return $ret; +} + +sub FILENO { + my ($self) = shift; + return *$self->{file}->fileno(@_) if defined *$self->{file}; + return; +} + +sub EOF { + # Since perl 5.12, an integer parameter is passed describing how the + # function got called, just ignore it. + my ($self, $param) = (shift, shift); + return *$self->{file}->eof(@_) if defined *$self->{file}; + return 1; +} + +sub SEEK { + my ($self) = shift; + return *$self->{file}->seek(@_) if defined *$self->{file}; + return 0; +} + +sub TELL { + my ($self) = shift; + return *$self->{file}->tell(@_) if defined *$self->{file}; + return -1; +} + +sub BINMODE { + my ($self) = shift; + return *$self->{file}->binmode(@_) if defined *$self->{file}; + return; +} + +## +## NORMAL METHODS +## + +=item $fh->set_compression($comp) + +Defines the compression method used. $comp should one of the methods supported by +B<Dpkg::Compression> or "none" or "auto". "none" indicates that the file is +uncompressed and "auto" indicates that the method must be guessed based +on the filename extension used. + +=cut + +sub set_compression { + my ($self, $method) = @_; + if ($method ne 'none' and $method ne 'auto') { + *$self->{compressor}->set_compression($method); + } + *$self->{compression} = $method; +} + +=item $fh->set_compression_level($level) + +Indicate the desired compression level. It should be a value accepted +by the function C<compression_is_valid_level> of B<Dpkg::Compression>. + +=cut + +sub set_compression_level { + my ($self, $level) = @_; + *$self->{compressor}->set_compression_level($level); +} + +=item $fh->set_filename($name, [$add_comp_ext]) + +Use $name as filename when the file must be opened/created. If +$add_comp_ext is passed, it indicates whether the default extension +of the compression method must be automatically added to the filename +(or not). + +=cut + +sub set_filename { + my ($self, $filename, $add_comp_ext) = @_; + *$self->{filename} = $filename; + # Automatically add compression extension to filename + if (defined($add_comp_ext)) { + *$self->{add_comp_ext} = $add_comp_ext; + } + my $comp_ext_regex = compression_get_file_extension_regex(); + if (*$self->{add_comp_ext} and $filename =~ /\.$comp_ext_regex$/) { + warning('filename %s already has an extension of a compressed file ' . + 'and add_comp_ext is active', $filename); + } +} + +=item my $file = $fh->get_filename() + +Returns the filename that would be used when the filehandle must +be opened (both in read and write mode). This function errors out +if "add_comp_ext" is enabled while the compression method is set +to "auto". The returned filename includes the extension of the compression +method if "add_comp_ext" is enabled. + +=cut + +sub get_filename { + my $self = shift; + my $comp = *$self->{compression}; + if (*$self->{add_comp_ext}) { + if ($comp eq 'auto') { + croak 'automatic detection of compression is ' . + 'incompatible with add_comp_ext'; + } elsif ($comp eq 'none') { + return *$self->{filename}; + } else { + return *$self->{filename} . '.' . + compression_get_property($comp, 'file_ext'); + } + } else { + return *$self->{filename}; + } +} + +=item $ret = $fh->use_compression() + +Returns "0" if no compression is used and the compression method used +otherwise. If the compression is set to "auto", the value returned +depends on the extension of the filename obtained with the B<get_filename> +method. + +=cut + +sub use_compression { + my ($self) = @_; + my $comp = *$self->{compression}; + if ($comp eq 'none') { + return 0; + } elsif ($comp eq 'auto') { + $comp = compression_guess_from_filename($self->get_filename()); + *$self->{compressor}->set_compression($comp) if $comp; + } + return $comp; +} + +=item my $real_fh = $fh->get_filehandle() + +Returns the real underlying filehandle. Useful if you want to pass it +along in a derived object. + +=cut + +sub get_filehandle { + my ($self) = @_; + return *$self->{file} if exists *$self->{file}; +} + +## INTERNAL METHODS + +sub open_for_write { + my ($self) = @_; + error("Can't reopen an already opened compressed file") if exists *$self->{mode}; + my $filehandle; + if ($self->use_compression()) { + *$self->{compressor}->compress(from_pipe => \$filehandle, + to_file => $self->get_filename()); + } else { + CORE::open($filehandle, '>', $self->get_filename) + or syserr(_g('cannot write %s'), $self->get_filename()); + } + *$self->{mode} = 'w'; + *$self->{file} = $filehandle; +} + +sub open_for_read { + my ($self) = @_; + error("Can't reopen an already opened compressed file") if exists *$self->{mode}; + my $filehandle; + if ($self->use_compression()) { + *$self->{compressor}->uncompress(to_pipe => \$filehandle, + from_file => $self->get_filename()); + *$self->{allow_sigpipe} = 1; + } else { + CORE::open($filehandle, '<', $self->get_filename) + or syserr(_g('cannot read %s'), $self->get_filename()); + } + *$self->{mode} = 'r'; + *$self->{file} = $filehandle; +} + +sub cleanup { + my ($self) = @_; + my $cmdline = *$self->{compressor}{cmdline} || ''; + *$self->{compressor}->wait_end_process(nocheck => *$self->{allow_sigpipe}); + if (*$self->{allow_sigpipe}) { + unless (($? == 0) || (WIFSIGNALED($?) && (WTERMSIG($?) == SIGPIPE))) { + subprocerr($cmdline); + } + *$self->{allow_sigpipe} = 0; + } + delete *$self->{mode}; + delete *$self->{file}; +} + +=back + +=head1 DERIVED OBJECTS + +If you want to create an object that inherits from +Dpkg::Compression::FileHandle you must be aware that +the object is a reference to a GLOB that is returned by Symbol::gensym() +and as such it's not a HASH. + +You can store internal data in a hash but you have to use +C<*$self->{...}> to access the associated hash like in the example below: + + sub set_option { + my ($self, $value) = @_; + *$self->{option} = $value; + } + + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org> + +=cut +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Compression/Process.pm b/third_party/dpkg-dev/scripts/Dpkg/Compression/Process.pm new file mode 100644 index 0000000..03d6f276 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Compression/Process.pm
@@ -0,0 +1,207 @@ +# Copyright © 2008-2010 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Compression::Process; + +use strict; +use warnings; + +our $VERSION = '1.00'; + +use Carp; + +use Dpkg::Compression; +use Dpkg::ErrorHandling; +use Dpkg::Gettext; +use Dpkg::IPC; + +=encoding utf8 + +=head1 NAME + +Dpkg::Compression::Process - run compression/decompression processes + +=head1 DESCRIPTION + +This module provides an object oriented interface to run and manage +compression/decompression processes. + +=head1 METHODS + +=over 4 + +=item my $proc = Dpkg::Compression::Process->new(%opts) + +Create a new instance of the object. Supported options are "compression" +and "compression_level" (see corresponding set_* functions). + +=cut + +sub new { + my ($this, %args) = @_; + my $class = ref($this) || $this; + my $self = {}; + bless $self, $class; + $self->set_compression($args{compression} || compression_get_default()); + $self->set_compression_level($args{compression_level} || + compression_get_default_level()); + return $self; +} + +=item $proc->set_compression($comp) + +Select the compression method to use. It errors out if the method is not +supported according to C<compression_is_supported> (of +B<Dpkg::Compression>). + +=cut + +sub set_compression { + my ($self, $method) = @_; + error(_g('%s is not a supported compression method'), $method) + unless compression_is_supported($method); + $self->{compression} = $method; +} + +=item $proc->set_compression_level($level) + +Select the compression level to use. It errors out if the level is not +valid according to C<compression_is_valid_level> (of +B<Dpkg::Compression>). + +=cut + +sub set_compression_level { + my ($self, $level) = @_; + error(_g('%s is not a compression level'), $level) + unless compression_is_valid_level($level); + $self->{compression_level} = $level; +} + +=item my @exec = $proc->get_compress_cmdline() + +=item my @exec = $proc->get_uncompress_cmdline() + +Returns a list ready to be passed to C<exec>, its first element is the +program name (either for compression or decompression) and the following +elements are parameters for the program. + +When executed the program acts as a filter between its standard input +and its standard output. + +=cut + +sub get_compress_cmdline { + my ($self) = @_; + my @prog = (@{compression_get_property($self->{compression}, 'comp_prog')}); + my $level = '-' . $self->{compression_level}; + $level = '--' . $self->{compression_level} + if $self->{compression_level} !~ m/^[1-9]$/; + push @prog, $level; + return @prog; +} + +sub get_uncompress_cmdline { + my ($self) = @_; + return (@{compression_get_property($self->{compression}, 'decomp_prog')}); +} + +sub _sanity_check { + my ($self, %opts) = @_; + # Check for proper cleaning before new start + error(_g('Dpkg::Compression::Process can only start one subprocess at a time')) + if $self->{pid}; + # Check options + my $to = my $from = 0; + foreach (qw(file handle string pipe)) { + $to++ if $opts{"to_$_"}; + $from++ if $opts{"from_$_"}; + } + croak 'exactly one to_* parameter is needed' if $to != 1; + croak 'exactly one from_* parameter is needed' if $from != 1; + return %opts; +} + +=item $proc->compress(%opts) + +Starts a compressor program. You must indicate where it will read its +uncompressed data from and where it will write its compressed data to. +This is accomplished by passing one parameter C<to_*> and one parameter +C<from_*> as accepted by B<Dpkg::IPC::spawn>. + +You must call C<wait_end_process> after having called this method to +properly close the sub-process (and verify that it exited without error). + +=cut + +sub compress { + my $self = shift; + my %opts = $self->_sanity_check(@_); + my @prog = $self->get_compress_cmdline(); + $opts{exec} = \@prog; + $self->{cmdline} = "@prog"; + $self->{pid} = spawn(%opts); + delete $self->{pid} if $opts{to_string}; # wait_child already done +} + +=item $proc->uncompress(%opts) + +Starts a decompressor program. You must indicate where it will read its +compressed data from and where it will write its uncompressed data to. +This is accomplished by passing one parameter C<to_*> and one parameter +C<from_*> as accepted by B<Dpkg::IPC::spawn>. + +You must call C<wait_end_process> after having called this method to +properly close the sub-process (and verify that it exited without error). + +=cut + +sub uncompress { + my $self = shift; + my %opts = $self->_sanity_check(@_); + my @prog = $self->get_uncompress_cmdline(); + $opts{exec} = \@prog; + $self->{cmdline} = "@prog"; + $self->{pid} = spawn(%opts); + delete $self->{pid} if $opts{to_string}; # wait_child already done +} + +=item $proc->wait_end_process(%opts) + +Call B<Dpkg::IPC::wait_child> to wait until the sub-process has exited +and verify its return code. Any given option will be forwarded to +the C<wait_child> function. Most notably you can use the "nocheck" option +to verify the return code yourself instead of letting C<wait_child> do +it for you. + +=cut + +sub wait_end_process { + my ($self, %opts) = @_; + $opts{cmdline} ||= $self->{cmdline}; + wait_child($self->{pid}, %opts) if $self->{pid}; + delete $self->{pid}; + delete $self->{cmdline}; +} + +=back + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Conf.pm b/third_party/dpkg-dev/scripts/Dpkg/Conf.pm new file mode 100644 index 0000000..0825d62 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Conf.pm
@@ -0,0 +1,184 @@ +# Copyright © 2009-2010 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Conf; + +use strict; +use warnings; + +our $VERSION = '1.01'; + +use Dpkg::Gettext; +use Dpkg::ErrorHandling; + +use parent qw(Dpkg::Interface::Storable); + +use overload + '@{}' => sub { return [ $_[0]->get_options() ] }, + fallback => 1; + +=encoding utf8 + +=head1 NAME + +Dpkg::Conf - parse dpkg configuration files + +=head1 DESCRIPTION + +The Dpkg::Conf object can be used to read options from a configuration +file. It can exports an array that can then be parsed exactly like @ARGV. + +=head1 FUNCTIONS + +=over 4 + +=item my $conf = Dpkg::Conf->new(%opts) + +Create a new Dpkg::Conf object. Some options can be set through %opts: +if allow_short evaluates to true (it defaults to false), then short +options are allowed in the configuration file, they should be prepended +with a single hyphen. + +=cut + +sub new { + my ($this, %opts) = @_; + my $class = ref($this) || $this; + + my $self = { + options => [], + allow_short => 0, + }; + foreach my $opt (keys %opts) { + $self->{$opt} = $opts{$opt}; + } + bless $self, $class; + + return $self; +} + +=item @$conf + +=item @options = $conf->get_options() + +Returns the list of options that can be parsed like @ARGV. + +=cut + +sub get_options { + my ($self) = @_; + return @{$self->{options}}; +} + +=item $conf->load($file) + +Read options from a file. Return the number of options parsed. + +=item $conf->parse($fh) + +Parse options from a file handle. Return the number of options parsed. + +=cut + +sub parse { + my ($self, $fh, $desc) = @_; + my $count = 0; + while (<$fh>) { + chomp; + s/^\s+//; s/\s+$//; # Strip leading/trailing spaces + s/\s+=\s+/=/; # Remove spaces around the first = + s/\s+/=/ unless m/=/; # First spaces becomes = if no = + next if /^#/ or /^$/; # Skip empty lines and comments + if (/^-[^-]/ and not $self->{allow_short}) { + warning(_g('short option not allowed in %s, line %d'), $desc, $.); + next; + } + if (/^([^=]+)(?:=(.*))?$/) { + my ($name, $value) = ($1, $2); + $name = "--$name" unless $name =~ /^-/; + if (defined $value) { + $value =~ s/^"(.*)"$/$1/ or $value =~ s/^'(.*)'$/$1/; + push @{$self->{options}}, "$name=$value"; + } else { + push @{$self->{options}}, $name; + } + $count++; + } else { + warning(_g('invalid syntax for option in %s, line %d'), $desc, $.); + } + } + return $count; +} + +=item $conf->filter(remove => $rmfunc) + +=item $conf->filter(keep => $keepfunc) + +Filter the list of options, either removing or keeping all those that +return true when &$rmfunc($option) or &keepfunc($option) is called. + +=cut + +sub filter { + my ($self, %opts) = @_; + if (defined($opts{remove})) { + @{$self->{options}} = grep { not &{$opts{remove}}($_) } + @{$self->{options}}; + } + if (defined($opts{keep})) { + @{$self->{options}} = grep { &{$opts{keep}}($_) } + @{$self->{options}}; + } +} + +=item $string = $conf->output($fh) + +Write the options in the given filehandle (if defined) and return a string +representation of the content (that would be) written. + +=item "$conf" + +Return a string representation of the content. + +=item $conf->save($file) + +Save the options in a file. + +=cut + +sub output { + my ($self, $fh) = @_; + my $ret = ''; + foreach my $opt ($self->get_options()) { + $opt =~ s/^--//; + if ($opt =~ s/^([^=]+)=/$1 = "/) { + $opt .= '"'; + } + $opt .= "\n"; + print { $fh } $opt if defined $fh; + $ret .= $opt; + } + return $ret; +} + +=back + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Control.pm b/third_party/dpkg-dev/scripts/Dpkg/Control.pm new file mode 100644 index 0000000..fae49d9 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Control.pm
@@ -0,0 +1,200 @@ +# Copyright © 2007-2009 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Control; + +use strict; +use warnings; + +our $VERSION = '1.00'; + +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Control::Types; +use Dpkg::Control::Hash; +use Dpkg::Control::Fields; + +use Exporter qw(import); + +use parent qw(Dpkg::Control::Hash); + +our @EXPORT = qw(CTRL_UNKNOWN CTRL_INFO_SRC CTRL_INFO_PKG CTRL_INDEX_SRC + CTRL_INDEX_PKG CTRL_PKG_SRC CTRL_PKG_DEB CTRL_FILE_CHANGES + CTRL_FILE_VENDOR CTRL_FILE_STATUS CTRL_CHANGELOG); + +=encoding utf8 + +=head1 NAME + +Dpkg::Control - parse and manipulate official control-like information + +=head1 DESCRIPTION + +The Dpkg::Control object is a smart version of Dpkg::Control::Hash. +It associates a type to the control information. That type can be +used to know what fields are allowed and in what order they must be +output. + +The types are constants that are exported by default. Here's the full +list: + +=over 4 + +=item CTRL_UNKNOWN + +This type is the default type, it indicates that the type of control +information is not yet known. + +=item CTRL_INFO_SRC + +Corresponds to the first block of information in a F<debian/control> file in +a Debian source package. + +=item CTRL_INFO_PKG + +Corresponds to subsequent blocks of information in a F<debian/control> file +in a Debian source package. + +=item CTRL_INDEX_SRC + +Corresponds to an entry in a F<Sources> file of a source package +repository. + +=item CTRL_INDEX_PKG + +Corresponds to an entry in a F<Packages> file of a binary package +repository. + +=item CTRL_PKG_SRC + +Corresponds to a .dsc file of a Debian source package. + +=item CTRL_PKG_DEB + +Corresponds to the F<control> file generated by dpkg-gencontrol +(F<DEBIAN/control>) and to the same file inside .deb packages. + +=item CTRL_FILE_CHANGES + +Corresponds to a .changes file. + +=item CTRL_FILE_VENDOR + +Corresponds to a vendor file in $Dpkg::CONFDIR/origins/. + +=item CTRL_FILE_STATUS + +Corresponds to an entry in dpkg's F<status> file ($Dpkg::ADMINDIR/status). + +=item CTRL_CHANGELOG + +Corresponds to the output of dpkg-parsechangelog. + +=back + +=head1 FUNCTIONS + +All the methods of Dpkg::Control::Hash are available. Those listed below +are either new or overridden with a different behaviour. + +=over 4 + +=item my $c = Dpkg::Control->new(%opts) + +If the "type" option is given, it's used to setup default values +for other options. See set_options() for more details. + +=cut + +sub new { + my ($this, %opts) = @_; + my $class = ref($this) || $this; + + my $self = Dpkg::Control::Hash->new(); + bless $self, $class; + $self->set_options(%opts); + + return $self; +} + +=item $c->set_options(%opts) + +Changes the value of one or more options. If the "type" option is changed, +it is used first to define default values for others options. The option +"allow_pgp" is set to 1 for CTRL_PKG_SRC and CTRL_FILE_CHANGES and to 0 +otherwise. The option "drop_empty" is set to 0 for CTRL_INFO_PKG and +CTRL_INFO_SRC and to 1 otherwise. The option "name" is set to a textual +description of the type of control information. + +The output order is also set to match the ordered list returned by +Dpkg::Control::Fields::field_ordered_list($type). + +=cut + +sub set_options { + my ($self, %opts) = @_; + if (exists $opts{type}) { + my $t = $opts{type}; + $$self->{allow_pgp} = ($t & (CTRL_PKG_SRC | CTRL_FILE_CHANGES)) ? 1 : 0; + $$self->{drop_empty} = ($t & (CTRL_INFO_PKG | CTRL_INFO_SRC)) ? 0 : 1; + if ($t == CTRL_INFO_SRC) { + $$self->{name} = _g('general section of control info file'); + } elsif ($t == CTRL_INFO_PKG) { + $$self->{name} = _g("package's section of control info file"); + } elsif ($t == CTRL_CHANGELOG) { + $$self->{name} = _g('parsed version of changelog'); + } elsif ($t == CTRL_INDEX_SRC) { + $$self->{name} = sprintf(_g("entry in repository's %s file"), 'Sources'); + } elsif ($t == CTRL_INDEX_PKG) { + $$self->{name} = sprintf(_g("entry in repository's %s file"), 'Packages'); + } elsif ($t == CTRL_PKG_SRC) { + $$self->{name} = sprintf(_g('%s file'), '.dsc'); + } elsif ($t == CTRL_PKG_DEB) { + $$self->{name} = _g('control info of a .deb package'); + } elsif ($t == CTRL_FILE_CHANGES) { + $$self->{name} = sprintf(_g('%s file'), '.changes'); + } elsif ($t == CTRL_FILE_VENDOR) { + $$self->{name} = _g('vendor file'); + } elsif ($t == CTRL_FILE_STATUS) { + $$self->{name} = _g("entry in dpkg's status file"); + } + $self->set_output_order(field_ordered_list($opts{type})); + } + + # Options set by the user override default values + $$self->{$_} = $opts{$_} foreach keys %opts; +} + +=item $c->get_type() + +Returns the type of control information stored. See the type parameter +set during new(). + +=cut + +sub get_type { + my ($self) = @_; + return $$self->{type}; +} + +=back + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Control/Changelog.pm b/third_party/dpkg-dev/scripts/Dpkg/Control/Changelog.pm new file mode 100644 index 0000000..a6890ea --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Control/Changelog.pm
@@ -0,0 +1,62 @@ +# Copyright © 2009 Raphaël Hertzog <hertzog@debian.org> + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Control::Changelog; + +use strict; +use warnings; + +our $VERSION = '1.00'; + +use Dpkg::Control; +use parent qw(Dpkg::Control); + +=encoding utf8 + +=head1 NAME + +Dpkg::Control::Changelog - represent info fields output by dpkg-parsechangelog + +=head1 DESCRIPTION + +This object derives directly from Dpkg::Control with the type +CTRL_CHANGELOG. + +=head1 FUNCTIONS + +=over 4 + +=item $c = Dpkg::Control::Changelog->new() + +Create a new empty set of changelog related fields. + +=cut + +sub new { + my $this = shift; + my $class = ref($this) || $this; + my $self = Dpkg::Control->new(type => CTRL_CHANGELOG, @_); + return bless $self, $class; +} + +=back + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Control/Fields.pm b/third_party/dpkg-dev/scripts/Dpkg/Control/Fields.pm new file mode 100644 index 0000000..bc175462 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Control/Fields.pm
@@ -0,0 +1,65 @@ +# Copyright © 2007-2009 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Control::Fields; + +use strict; +use warnings; + +our $VERSION = '1.00'; + +use Carp; +use Exporter qw(import); + +use Dpkg::Control::FieldsCore; +use Dpkg::Vendor qw(run_vendor_hook); + +our @EXPORT = @Dpkg::Control::FieldsCore::EXPORT; + +# Register vendor specifics fields +foreach my $op (run_vendor_hook('register-custom-fields')) { + next if not (defined $op and ref $op); # Skip when not implemented by vendor + my $func = shift @$op; + if ($func eq 'register') { + &field_register(@$op); + } elsif ($func eq 'insert_before') { + &field_insert_before(@$op); + } elsif ($func eq 'insert_after') { + &field_insert_after(@$op); + } else { + croak "vendor hook register-custom-fields sent bad data: @$op"; + } +} + +=encoding utf8 + +=head1 NAME + +Dpkg::Control::Fields - manage (list of official) control fields + +=head1 DESCRIPTION + +The module contains a list of vendor-neutral and vendor-specific fieldnames +with associated meta-data explaining in which type of control information +they are allowed. The vendor-neutral fieldnames and all functions are +inherited from Dpkg::Control::FieldsCore. + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Control/FieldsCore.pm b/third_party/dpkg-dev/scripts/Dpkg/Control/FieldsCore.pm new file mode 100644 index 0000000..1ae32086 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Control/FieldsCore.pm
@@ -0,0 +1,660 @@ +# Copyright © 2007-2009 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Control::FieldsCore; + +use strict; +use warnings; + +our $VERSION = '1.00'; + +use Exporter qw(import); +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Control::Types; +use Dpkg::Checksums; + +our @EXPORT = qw(field_capitalize field_is_official field_is_allowed_in + field_transfer_single field_transfer_all + field_list_src_dep field_list_pkg_dep field_get_dep_type + field_get_sep_type + field_ordered_list field_register + field_insert_after field_insert_before + FIELD_SEP_UNKNOWN FIELD_SEP_SPACE FIELD_SEP_COMMA + FIELD_SEP_LINE); + +use constant { + ALL_PKG => CTRL_INFO_PKG | CTRL_INDEX_PKG | CTRL_PKG_DEB | CTRL_FILE_STATUS, + ALL_SRC => CTRL_INFO_SRC | CTRL_INDEX_SRC | CTRL_PKG_SRC, + ALL_CHANGES => CTRL_FILE_CHANGES | CTRL_CHANGELOG, +}; + +use constant { + FIELD_SEP_UNKNOWN => 0, + FIELD_SEP_SPACE => 1, + FIELD_SEP_COMMA => 2, + FIELD_SEP_LINE => 4, +}; + +# The canonical list of fields + +# Note that fields used only in dpkg's available file are not listed +# Deprecated fields of dpkg's status file are also not listed +our %FIELDS = ( + 'Architecture' => { + allowed => (ALL_PKG | ALL_SRC | CTRL_FILE_CHANGES) & (~CTRL_INFO_SRC), + separator => FIELD_SEP_SPACE, + }, + 'Binary' => { + allowed => CTRL_PKG_SRC | CTRL_FILE_CHANGES, + # XXX: This field values are separated either by space or comma + # depending on the context. + separator => FIELD_SEP_SPACE | FIELD_SEP_COMMA, + }, + 'Binary-Only' => { + allowed => ALL_CHANGES, + }, + 'Breaks' => { + allowed => ALL_PKG, + separator => FIELD_SEP_COMMA, + dependency => 'union', + dep_order => 7, + }, + 'Bugs' => { + allowed => (ALL_PKG | CTRL_INFO_SRC | CTRL_FILE_VENDOR) & (~CTRL_INFO_PKG), + }, + 'Build-Conflicts' => { + allowed => ALL_SRC, + separator => FIELD_SEP_COMMA, + dependency => 'union', + dep_order => 4, + }, + 'Build-Conflicts-Arch' => { + allowed => ALL_SRC, + separator => FIELD_SEP_COMMA, + dependency => 'union', + dep_order => 5, + }, + 'Build-Conflicts-Indep' => { + allowed => ALL_SRC, + separator => FIELD_SEP_COMMA, + dependency => 'union', + dep_order => 6, + }, + 'Build-Depends' => { + allowed => ALL_SRC, + separator => FIELD_SEP_COMMA, + dependency => 'normal', + dep_order => 1, + }, + 'Build-Depends-Arch' => { + allowed => ALL_SRC, + separator => FIELD_SEP_COMMA, + dependency => 'normal', + dep_order => 2, + }, + 'Build-Depends-Indep' => { + allowed => ALL_SRC, + separator => FIELD_SEP_COMMA, + dependency => 'normal', + dep_order => 3, + }, + 'Built-For-Profiles' => { + allowed => ALL_PKG | CTRL_FILE_CHANGES, + separator => FIELD_SEP_SPACE, + }, + 'Built-Using' => { + allowed => ALL_PKG, + separator => FIELD_SEP_COMMA, + dependency => 'union', + dep_order => 10, + }, + 'Changed-By' => { + allowed => CTRL_FILE_CHANGES, + }, + 'Changes' => { + allowed => ALL_CHANGES, + }, + 'Closes' => { + allowed => ALL_CHANGES, + separator => FIELD_SEP_SPACE, + }, + 'Conffiles' => { + allowed => CTRL_FILE_STATUS, + separator => FIELD_SEP_LINE | FIELD_SEP_SPACE, + }, + 'Config-Version' => { + allowed => CTRL_FILE_STATUS, + }, + 'Conflicts' => { + allowed => ALL_PKG, + separator => FIELD_SEP_COMMA, + dependency => 'union', + dep_order => 6, + }, + 'Date' => { + allowed => ALL_CHANGES, + }, + 'Depends' => { + allowed => ALL_PKG, + separator => FIELD_SEP_COMMA, + dependency => 'normal', + dep_order => 2, + }, + 'Description' => { + allowed => ALL_PKG | CTRL_FILE_CHANGES, + }, + 'Directory' => { + allowed => CTRL_INDEX_SRC, + }, + 'Distribution' => { + allowed => ALL_CHANGES, + }, + 'Enhances' => { + allowed => ALL_PKG, + separator => FIELD_SEP_COMMA, + dependency => 'union', + dep_order => 5, + }, + 'Essential' => { + allowed => ALL_PKG, + }, + 'Filename' => { + allowed => CTRL_INDEX_PKG, + separator => FIELD_SEP_LINE | FIELD_SEP_SPACE, + }, + 'Files' => { + allowed => CTRL_PKG_SRC | CTRL_FILE_CHANGES, + separator => FIELD_SEP_LINE | FIELD_SEP_SPACE, + }, + 'Format' => { + allowed => CTRL_PKG_SRC | CTRL_FILE_CHANGES, + }, + 'Homepage' => { + allowed => ALL_SRC | ALL_PKG, + }, + 'Installed-Size' => { + allowed => ALL_PKG & ~CTRL_INFO_PKG, + }, + 'Installer-Menu-Item' => { + allowed => ALL_PKG, + }, + 'Kernel-Version' => { + allowed => ALL_PKG, + }, + 'Origin' => { + allowed => (ALL_PKG | ALL_SRC) & (~CTRL_INFO_PKG), + }, + 'Maintainer' => { + allowed => CTRL_PKG_DEB | ALL_SRC | ALL_CHANGES, + }, + 'Multi-Arch' => { + allowed => ALL_PKG, + }, + 'Package' => { + allowed => ALL_PKG, + }, + 'Package-List' => { + allowed => ALL_SRC & ~CTRL_INFO_SRC, + separator => FIELD_SEP_LINE | FIELD_SEP_SPACE, + }, + 'Package-Type' => { + allowed => ALL_PKG, + }, + 'Parent' => { + allowed => CTRL_FILE_VENDOR, + }, + 'Pre-Depends' => { + allowed => ALL_PKG, + separator => FIELD_SEP_COMMA, + dependency => 'normal', + dep_order => 1, + }, + 'Priority' => { + allowed => CTRL_INFO_SRC | CTRL_INDEX_SRC | ALL_PKG, + }, + 'Provides' => { + allowed => ALL_PKG, + separator => FIELD_SEP_COMMA, + dependency => 'union', + dep_order => 9, + }, + 'Recommends' => { + allowed => ALL_PKG, + separator => FIELD_SEP_COMMA, + dependency => 'normal', + dep_order => 3, + }, + 'Replaces' => { + allowed => ALL_PKG, + separator => FIELD_SEP_COMMA, + dependency => 'union', + dep_order => 8, + }, + 'Section' => { + allowed => CTRL_INFO_SRC | CTRL_INDEX_SRC | ALL_PKG, + }, + 'Size' => { + allowed => CTRL_INDEX_PKG, + separator => FIELD_SEP_LINE | FIELD_SEP_SPACE, + }, + 'Source' => { + allowed => (ALL_PKG | ALL_SRC | ALL_CHANGES) & + (~(CTRL_INDEX_SRC | CTRL_INFO_PKG)), + }, + 'Standards-Version' => { + allowed => ALL_SRC, + }, + 'Status' => { + allowed => CTRL_FILE_STATUS, + separator => FIELD_SEP_SPACE, + }, + 'Subarchitecture' => { + allowed => ALL_PKG, + }, + 'Suggests' => { + allowed => ALL_PKG, + separator => FIELD_SEP_COMMA, + dependency => 'normal', + dep_order => 4, + }, + 'Tag' => { + allowed => ALL_PKG, + separator => FIELD_SEP_COMMA, + }, + 'Task' => { + allowed => ALL_PKG, + }, + 'Triggers-Awaited' => { + allowed => CTRL_FILE_STATUS, + separator => FIELD_SEP_SPACE, + }, + 'Triggers-Pending' => { + allowed => CTRL_FILE_STATUS, + separator => FIELD_SEP_SPACE, + }, + 'Uploaders' => { + allowed => ALL_SRC, + separator => FIELD_SEP_COMMA, + }, + 'Urgency' => { + allowed => ALL_CHANGES, + }, + 'Vcs-Browser' => { + allowed => ALL_SRC, + }, + 'Vcs-Arch' => { + allowed => ALL_SRC, + }, + 'Vcs-Bzr' => { + allowed => ALL_SRC, + }, + 'Vcs-Cvs' => { + allowed => ALL_SRC, + }, + 'Vcs-Darcs' => { + allowed => ALL_SRC, + }, + 'Vcs-Git' => { + allowed => ALL_SRC, + }, + 'Vcs-Hg' => { + allowed => ALL_SRC, + }, + 'Vcs-Mtn' => { + allowed => ALL_SRC, + }, + 'Vcs-Svn' => { + allowed => ALL_SRC, + }, + 'Vendor' => { + allowed => CTRL_FILE_VENDOR, + }, + 'Vendor-Url' => { + allowed => CTRL_FILE_VENDOR, + }, + 'Version' => { + allowed => (ALL_PKG | ALL_SRC | ALL_CHANGES) & + (~(CTRL_INFO_SRC | CTRL_INFO_PKG)), + }, +); + +my @checksum_fields = map { &field_capitalize("Checksums-$_") } checksums_get_list(); +my @sum_fields = map { $_ eq 'md5' ? 'MD5sum' : &field_capitalize($_) } + checksums_get_list(); +&field_register($_, CTRL_PKG_SRC | CTRL_FILE_CHANGES) foreach @checksum_fields; +&field_register($_, CTRL_INDEX_PKG, + separator => FIELD_SEP_LINE | FIELD_SEP_SPACE) foreach @sum_fields; + +our %FIELD_ORDER = ( + CTRL_PKG_DEB() => [ + qw(Package Package-Type Source Version Built-Using Kernel-Version + Built-For-Profiles Architecture Subarchitecture + Installer-Menu-Item Essential Origin Bugs + Maintainer Installed-Size), &field_list_pkg_dep(), + qw(Section Priority Multi-Arch Homepage Description Tag Task) + ], + CTRL_PKG_SRC() => [ + qw(Format Source Binary Architecture Version Origin Maintainer + Uploaders Homepage Standards-Version Vcs-Browser + Vcs-Arch Vcs-Bzr Vcs-Cvs Vcs-Darcs Vcs-Git Vcs-Hg Vcs-Mtn + Vcs-Svn), &field_list_src_dep(), qw(Package-List), + @checksum_fields, qw(Files) + ], + CTRL_FILE_CHANGES() => [ + qw(Format Date Source Binary Binary-Only Built-For-Profiles Architecture + Version Distribution Urgency Maintainer Changed-By Description + Closes Changes), + @checksum_fields, qw(Files) + ], + CTRL_CHANGELOG() => [ + qw(Source Binary-Only Version Distribution Urgency Maintainer + Date Closes Changes) + ], + CTRL_FILE_STATUS() => [ # Same as fieldinfos in lib/dpkg/parse.c + qw(Package Essential Status Priority Section Installed-Size Origin + Maintainer Bugs Architecture Multi-Arch Source Version Config-Version + Replaces Provides Depends Pre-Depends Recommends Suggests Breaks + Conflicts Enhances Conffiles Description Triggers-Pending + Triggers-Awaited) + ], +); +# Order for CTRL_INDEX_PKG is derived from CTRL_PKG_DEB +$FIELD_ORDER{CTRL_INDEX_PKG()} = [ @{$FIELD_ORDER{CTRL_PKG_DEB()}} ]; +&field_insert_before(CTRL_INDEX_PKG, 'Section', 'Filename', 'Size', @sum_fields); +# Order for CTRL_INDEX_SRC is derived from CTRL_PKG_SRC +$FIELD_ORDER{CTRL_INDEX_SRC()} = [ @{$FIELD_ORDER{CTRL_PKG_SRC()}} ]; +@{$FIELD_ORDER{CTRL_INDEX_SRC()}} = map { $_ eq 'Source' ? 'Package' : $_ } + @{$FIELD_ORDER{CTRL_PKG_SRC()}}; +&field_insert_after(CTRL_INDEX_SRC, 'Version', 'Priority', 'Section'); +&field_insert_before(CTRL_INDEX_SRC, 'Checksums-Md5', 'Directory'); + +=encoding utf8 + +=head1 NAME + +Dpkg::Control::FieldsCore - manage (list of official) control fields + +=head1 DESCRIPTION + +The modules contains a list of fieldnames with associated meta-data explaining +in which type of control information they are allowed. The types are the +CTRL_* constants exported by Dpkg::Control. + +=head1 FUNCTIONS + +=over 4 + +=item my $f = field_capitalize($field_name) + +Returns the field name properly capitalized. All characters are lowercase, +except the first of each word (words are separated by a hyphen in field names). + +=cut + +sub field_capitalize($) { + my $field = lc(shift); + # Some special cases due to history + return 'MD5sum' if $field eq 'md5sum'; + return uc($field) if checksums_is_supported($field); + # Generic case + return join '-', map { ucfirst } split /-/, $field; +} + +=item field_is_official($fname) + +Returns true if the field is official and known. + +=cut + +sub field_is_official($) { + return exists $FIELDS{field_capitalize($_[0])}; +} + +=item field_is_allowed_in($fname, @types) + +Returns true (1) if the field $fname is allowed in all the types listed in +the list. Note that you can use type sets instead of individual types (ex: +CTRL_FILE_CHANGES | CTRL_CHANGELOG). + +field_allowed_in(A|B, C) returns true only if the field is allowed in C +and either A or B. + +Undef is returned for non-official fields. + +=cut + +sub field_is_allowed_in($@) { + my ($field, @types) = @_; + $field = field_capitalize($field); + return unless field_is_official($field); + + return 0 if not scalar(@types); + foreach my $type (@types) { + next if $type == CTRL_UNKNOWN; # Always allowed + return 0 unless $FIELDS{$field}{allowed} & $type; + } + return 1; +} + +=item field_transfer_single($from, $to, $field) + +If appropriate, copy the value of the field named $field taken from the +$from Dpkg::Control object to the $to Dpkg::Control object. + +Official fields are copied only if the field is allowed in both types of +objects. Custom fields are treated in a specific manner. When the target +is not among CTRL_PKG_SRC, CTRL_PKG_DEB or CTRL_FILE_CHANGES, then they +are alway copied as is (the X- prefix is kept). Otherwise they are not +copied except if the target object matches the target destination encoded +in the field name. The initial X denoting custom fields can be followed by +one or more letters among "S" (Source: corresponds to CTRL_PKG_SRC), "B" +(Binary: corresponds to CTRL_PKG_DEB) or "C" (Changes: corresponds to +CTRL_FILE_CHANGES). + +Returns undef if nothing has been copied or the name of the new field +added to $to otherwise. + +=cut + +sub field_transfer_single($$;$) { + my ($from, $to, $field) = @_; + $field //= $_; + my ($from_type, $to_type) = ($from->get_type(), $to->get_type()); + $field = field_capitalize($field); + + if (field_is_allowed_in($field, $from_type, $to_type)) { + $to->{$field} = $from->{$field}; + return $field; + } elsif ($field =~ /^X([SBC]*)-/i) { + my $dest = $1; + if (($dest =~ /B/i and $to_type == CTRL_PKG_DEB) or + ($dest =~ /S/i and $to_type == CTRL_PKG_SRC) or + ($dest =~ /C/i and $to_type == CTRL_FILE_CHANGES)) + { + my $new = $field; + $new =~ s/^X([SBC]*)-//i; + $to->{$new} = $from->{$field}; + return $new; + } elsif ($to_type != CTRL_PKG_DEB and + $to_type != CTRL_PKG_SRC and + $to_type != CTRL_FILE_CHANGES) + { + $to->{$field} = $from->{$field}; + return $field; + } + } elsif (not field_is_allowed_in($field, $from_type)) { + warning(_g("unknown information field '%s' in input data in %s"), + $field, $from->get_option('name') || _g('control information')); + } + return; +} + +=item field_transfer_all($from, $to) + +Transfer all appropriate fields from $from to $to. Calls +field_transfer_single() on all fields available in $from. + +Returns the list of fields that have been added to $to. + +=cut + +sub field_transfer_all($$) { + my ($from, $to) = @_; + my (@res, $res); + foreach my $k (keys %$from) { + $res = field_transfer_single($from, $to, $k); + push @res, $res if $res and defined wantarray; + } + return @res; +} + +=item field_ordered_list($type) + +Returns an ordered list of fields for a given type of control information. +This list can be used to output the fields in a predictable order. +The list might be empty for types where the order does not matter much. + +=cut + +sub field_ordered_list($) { + my ($type) = @_; + return @{$FIELD_ORDER{$type}} if exists $FIELD_ORDER{$type}; + return (); +} + +=item field_list_src_dep() + +List of fields that contains dependencies-like information in a source +Debian package. + +=cut + +sub field_list_src_dep() { + my @list = sort { + $FIELDS{$a}{dep_order} <=> $FIELDS{$b}{dep_order} + } grep { + field_is_allowed_in($_, CTRL_PKG_SRC) and + exists $FIELDS{$_}{dependency} + } keys %FIELDS; + return @list; +} + +=item field_list_pkg_dep() + +List of fields that contains dependencies-like information in a binary +Debian package. The fields that express real dependencies are sorted from +the stronger to the weaker. + +=cut + +sub field_list_pkg_dep() { + my @keys = keys %FIELDS; + my @list = sort { + $FIELDS{$a}{dep_order} <=> $FIELDS{$b}{dep_order} + } grep { + field_is_allowed_in($_, CTRL_PKG_DEB) and + exists $FIELDS{$_}{dependency} + } @keys; + return @list; +} + +=item field_get_dep_type($field) + +Return the type of the dependency expressed by the given field. Can +either be "normal" for a real dependency field (Pre-Depends, Depends, ...) +or "union" for other relation fields sharing the same syntax (Conflicts, +Breaks, ...). Returns undef for fields which are not dependencies. + +=cut + +sub field_get_dep_type($) { + my $field = field_capitalize($_[0]); + return unless field_is_official($field); + return $FIELDS{$field}{dependency} if exists $FIELDS{$field}{dependency}; + return; +} + +=item field_get_sep_type($field) + +Return the type of the field value separator. Can be one of FIELD_SEP_UNKNOWN, +FIELD_SEP_SPACE, FIELD_SEP_COMMA or FIELD_SEP_LINE. + +=cut + +sub field_get_sep_type($) { + my $field = field_capitalize($_[0]); + + return $FIELDS{$field}{separator} if exists $FIELDS{$field}{separator}; + return FIELD_SEP_UNKNOWN; +} + +=item field_register($field, $allowed_types, %opts) + +Register a new field as being allowed in control information of specified +types. %opts is optional + +=cut + +sub field_register($$;@) { + my ($field, $types, %opts) = @_; + $field = field_capitalize($field); + $FIELDS{$field} = { + allowed => $types, + %opts + }; +} + +=item field_insert_after($type, $ref, @fields) + +Place field after another one ($ref) in output of control information of +type $type. + +=cut +sub field_insert_after($$@) { + my ($type, $field, @fields) = @_; + return 0 if not exists $FIELD_ORDER{$type}; + ($field, @fields) = map { field_capitalize($_) } ($field, @fields); + @{$FIELD_ORDER{$type}} = map { + ($_ eq $field) ? ($_, @fields) : $_ + } @{$FIELD_ORDER{$type}}; + return 1; +} + +=item field_insert_before($type, $ref, @fields) + +Place field before another one ($ref) in output of control information of +type $type. + +=cut +sub field_insert_before($$@) { + my ($type, $field, @fields) = @_; + return 0 if not exists $FIELD_ORDER{$type}; + ($field, @fields) = map { field_capitalize($_) } ($field, @fields); + @{$FIELD_ORDER{$type}} = map { + ($_ eq $field) ? (@fields, $_) : $_ + } @{$FIELD_ORDER{$type}}; + return 1; +} + +=back + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Control/Hash.pm b/third_party/dpkg-dev/scripts/Dpkg/Control/Hash.pm new file mode 100644 index 0000000..85eccac --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Control/Hash.pm
@@ -0,0 +1,46 @@ +# Copyright © 2007-2009 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Control::Hash; + +use strict; +use warnings; + +our $VERSION = '1.00'; + +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Control::Fields; # Force execution of vendor hook. + +use parent qw(Dpkg::Control::HashCore); + +=encoding utf8 + +=head1 NAME + +Dpkg::Control::Hash - parse and manipulate a block of RFC822-like fields + +=head1 DESCRIPTION + +This module is just like Dpkg::Control::HashCore, with vendor-specific +field knowledge. + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Control/HashCore.pm b/third_party/dpkg-dev/scripts/Dpkg/Control/HashCore.pm new file mode 100644 index 0000000..e2ceb66 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Control/HashCore.pm
@@ -0,0 +1,540 @@ +# Copyright © 2007-2009 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Control::HashCore; + +use strict; +use warnings; + +our $VERSION = '1.01'; + +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Control::FieldsCore; + +# This module cannot use Dpkg::Control::Fields, because that one makes use +# of Dpkg::Vendor which at the same time uses this module, which would turn +# into a compilation error. We can use Dpkg::Control::FieldsCore instead. + +use parent qw(Dpkg::Interface::Storable); + +use overload + '%{}' => sub { ${$_[0]}->{fields} }, + 'eq' => sub { "$_[0]" eq "$_[1]" }; + +=encoding utf8 + +=head1 NAME + +Dpkg::Control::HashCore - parse and manipulate a block of RFC822-like fields + +=head1 DESCRIPTION + +The Dpkg::Control::Hash object is a hash-like representation of a set of +RFC822-like fields. The fields names are case insensitive and are always +capitalized the same when output (see field_capitalize function in +Dpkg::Control::Fields). +The order in which fields have been set is remembered and is used +to be able to dump back the same content. The output order can also be +overridden if needed. + +You can store arbitrary values in the hash, they will always be properly +escaped in the output to conform to the syntax of control files. This is +relevant mainly for multilines values: while the first line is always output +unchanged directly after the field name, supplementary lines are +modified. Empty lines and lines containing only dots are prefixed with +" ." (space + dot) while other lines are prefixed with a single space. + +During parsing, trailing spaces are stripped on all lines while leading +spaces are stripped only on the first line of each field. + +=head1 FUNCTIONS + +=over 4 + +=item my $c = Dpkg::Control::Hash->new(%opts) + +Creates a new object with the indicated options. Supported options +are: + +=over 8 + +=item allow_pgp + +Configures the parser to accept PGP signatures around the control +information. Value can be 0 (default) or 1. + +=item allow_duplicate + +Configures the parser to allow duplicate fields in the control +information. Value can be 0 (default) or 1. + +=item drop_empty + +Defines if empty fields are dropped during the output. Value can be 0 +(default) or 1. + +=item name + +The user friendly name of the information stored in the object. It might +be used in some error messages or warnings. A default name might be set +depending on the type. + +=back + +=cut + +sub new { + my ($this, %opts) = @_; + my $class = ref($this) || $this; + + # Object is a scalar reference and not a hash ref to avoid + # infinite recursion due to overloading hash-derefencing + my $self = \{ + in_order => [], + out_order => [], + is_pgp_signed => 0, + allow_pgp => 0, + allow_duplicate => 0, + drop_empty => 0, + }; + bless $self, $class; + + $$self->{fields} = Dpkg::Control::HashCore::Tie->new($self); + + # Options set by the user override default values + $$self->{$_} = $opts{$_} foreach keys %opts; + + return $self; +} + +# There is naturally a circular reference between the tied hash and its +# containing object. Happily, the extra layer of scalar reference can +# be used to detect the destruction of the object and break the loop so +# that everything gets garbage-collected. + +sub DESTROY { + my ($self) = @_; + delete $$self->{fields}; +} + +=item $c->set_options($option, %opts) + +Changes the value of one or more options. + +=cut + +sub set_options { + my ($self, %opts) = @_; + $$self->{$_} = $opts{$_} foreach keys %opts; +} + +=item my $value = $c->get_option($option) + +Returns the value of the corresponding option. + +=cut + +sub get_option { + my ($self, $k) = @_; + return $$self->{$k}; +} + +=item $c->load($file) + +Parse the content of $file. Exits in case of errors. Returns true if some +fields have been parsed. + +=item $c->parse_error($file, $fmt, ...) + +Prints an error message and dies on syntax parse errors. + +=cut + +sub parse_error { + my ($self, $file, $msg) = (shift, shift, shift); + + $msg = sprintf($msg, @_) if (@_); + error(_g('syntax error in %s at line %d: %s'), $file, $., $msg); +} + +=item $c->parse($fh, $description) + +Parse a control file from the given filehandle. Exits in case of errors. +$description is used to describe the filehandle, ideally it's a filename +or a description of where the data comes from. It's used in error +messages. Returns true if some fields have been parsed. + +=cut + +sub parse { + my ($self, $fh, $desc) = @_; + + my $paraborder = 1; + my $parabody = 0; + my $cf; # Current field + my $expect_pgp_sig = 0; + + while (<$fh>) { + chomp; + next if m/^\s*$/ and $paraborder; + next if (m/^#/); + $paraborder = 0; + if (m/^(\S+?)\s*:\s*(.*)$/) { + $parabody = 1; + if ($1 =~ m/^-/) { + $self->parse_error($desc, _g('field cannot start with a hyphen')); + } + my ($name, $value) = ($1, $2); + if (exists $self->{$name}) { + unless ($$self->{allow_duplicate}) { + $self->parse_error($desc, _g('duplicate field %s found'), $name); + } + } + $value =~ s/\s*$//; + $self->{$name} = $value; + $cf = $name; + } elsif (m/^\s(\s*\S.*)$/) { + my $line = $1; + unless (defined($cf)) { + $self->parse_error($desc, _g('continued value line not in field')); + } + if ($line =~ /^\.+$/) { + $line = substr $line, 1; + } + $line =~ s/\s*$//; + $self->{$cf} .= "\n$line"; + } elsif (m/^-----BEGIN PGP SIGNED MESSAGE-----[\r\t ]*$/) { + $expect_pgp_sig = 1; + if ($$self->{allow_pgp} and not $parabody) { + # Skip PGP headers + while (<$fh>) { + last if m/^\s*$/; + } + } else { + $self->parse_error($desc, _g('PGP signature not allowed here')); + } + } elsif (m/^\s*$/ || + ($expect_pgp_sig && m/^-----BEGIN PGP SIGNATURE-----[\r\t ]*$/)) { + if ($expect_pgp_sig) { + # Skip empty lines + $_ = <$fh> while defined($_) && $_ =~ /^\s*$/; + unless (length $_) { + $self->parse_error($desc, _g('expected PGP signature, ' . + 'found EOF after blank line')); + } + chomp; + unless (m/^-----BEGIN PGP SIGNATURE-----[\r\t ]*$/) { + $self->parse_error($desc, _g('expected PGP signature, ' . + "found something else \`%s'"), $_); + } + # Skip PGP signature + while (<$fh>) { + chomp; + last if m/^-----END PGP SIGNATURE-----[\r\t ]*$/; + } + unless (defined($_)) { + $self->parse_error($desc, _g('unfinished PGP signature')); + } + # This does not mean the signature is correct, that needs to + # be verified by gnupg. + $$self->{is_pgp_signed} = 1; + } + last; # Finished parsing one block + } else { + $self->parse_error($desc, + _g('line with unknown format (not field-colon-value)')); + } + } + + if ($expect_pgp_sig and not $$self->{is_pgp_signed}) { + $self->parse_error($desc, _g('unfinished PGP signature')); + } + + return defined($cf); +} + +=item $c->find_custom_field($name) + +Scan the fields and look for a user specific field whose name matches the +following regex: /X[SBC]*-$name/i. Return the name of the field found or +undef if nothing has been found. + +=cut + +sub find_custom_field { + my ($self, $name) = @_; + foreach my $key (keys %$self) { + return $key if $key =~ /^X[SBC]*-\Q$name\E$/i; + } + return; +} + +=item $c->get_custom_field($name) + +Identify a user field and retrieve its value. + +=cut + +sub get_custom_field { + my ($self, $name) = @_; + my $key = $self->find_custom_field($name); + return $self->{$key} if defined $key; + return; +} + +=item $c->save($filename) + +Write the string representation of the control information to a +file. + +=item my $str = $c->output() + +=item "$c" + +Get a string representation of the control information. The fields +are sorted in the order in which they have been read or set except +if the order has been overridden with set_output_order(). + +=item $c->output($fh) + +Print the string representation of the control information to a +filehandle. + +=cut + +sub output { + my ($self, $fh) = @_; + my $str = ''; + my @keys; + if (@{$$self->{out_order}}) { + my $i = 1; + my $imp = {}; + $imp->{$_} = $i++ foreach @{$$self->{out_order}}; + @keys = sort { + if (defined $imp->{$a} && defined $imp->{$b}) { + $imp->{$a} <=> $imp->{$b}; + } elsif (defined($imp->{$a})) { + -1; + } elsif (defined($imp->{$b})) { + 1; + } else { + $a cmp $b; + } + } keys %$self; + } else { + @keys = @{$$self->{in_order}}; + } + + foreach my $key (@keys) { + if (exists $self->{$key}) { + my $value = $self->{$key}; + # Skip whitespace-only fields + next if $$self->{drop_empty} and $value !~ m/\S/; + # Escape data to follow control file syntax + my @lines = split(/\n/, $value); + $value = (scalar @lines) ? shift @lines : ''; + foreach (@lines) { + s/\s+$//; + if (/^$/ or /^\.+$/) { + $value .= "\n .$_"; + } else { + $value .= "\n $_"; + } + } + # Print it out + if ($fh) { + print { $fh } "$key: $value\n" + or syserr(_g('write error on control data')); + } + $str .= "$key: $value\n" if defined wantarray; + } + } + return $str; +} + +=item $c->set_output_order(@fields) + +Define the order in which fields will be displayed in the output() method. + +=cut + +sub set_output_order { + my ($self, @fields) = @_; + + $$self->{out_order} = [@fields]; +} + +=item $c->apply_substvars($substvars) + +Update all fields by replacing the variables references with +the corresponding value stored in the Dpkg::Substvars object. + +=cut + +sub apply_substvars { + my ($self, $substvars, %opts) = @_; + + # Add substvars to refer to other fields + foreach my $f (keys %$self) { + $substvars->set_as_used("F:$f", $self->{$f}); + } + + foreach my $f (keys %$self) { + my $v = $substvars->substvars($self->{$f}, %opts); + if ($v ne $self->{$f}) { + my $sep; + + $sep = field_get_sep_type($f); + + # If we replaced stuff, ensure we're not breaking + # a dependency field by introducing empty lines, or multiple + # commas + + if ($sep & (FIELD_SEP_COMMA | FIELD_SEP_LINE)) { + # Drop empty/whitespace-only lines + $v =~ s/\n[ \t]*(\n|$)/$1/; + } + + if ($sep & FIELD_SEP_COMMA) { + $v =~ s/,[\s,]*,/,/g; + $v =~ s/^\s*,\s*//; + $v =~ s/\s*,\s*$//; + } + } + $v =~ s/\$\{\}/\$/g; # XXX: what for? + + $self->{$f} = $v; + } +} + +package Dpkg::Control::HashCore::Tie; + +# This object is used to tie a hash. It implements hash-like functions by +# normalizing the name of fields received in keys (using +# Dpkg::Control::Fields::field_capitalize). It also stores the order in +# which fields have been added in order to be able to dump them in the +# same order. But the order information is stored in a parent object of +# type Dpkg::Control. + +use Dpkg::Checksums; +use Dpkg::Control::FieldsCore; + +use Carp; +use Tie::Hash; +use parent -norequire, qw(Tie::ExtraHash); + +# $self->[0] is the real hash +# $self->[1] is a reference to the hash contained by the parent object. +# This reference bypasses the top-level scalar reference of a +# Dpkg::Control::Hash, hence ensuring that that reference gets DESTROYed +# properly. + +# Dpkg::Control::Hash->new($parent) +# +# Return a reference to a tied hash implementing storage of simple +# "field: value" mapping as used in many Debian-specific files. + +sub new { + my $class = shift; + my $hash = {}; + tie %{$hash}, $class, @_; + return $hash; +} + +sub TIEHASH { + my ($class, $parent) = @_; + croak 'parent object must be Dpkg::Control::Hash' + if not $parent->isa('Dpkg::Control::HashCore') and + not $parent->isa('Dpkg::Control::Hash'); + return bless [ {}, $$parent ], $class; +} + +sub FETCH { + my ($self, $key) = @_; + $key = lc($key); + return $self->[0]->{$key} if exists $self->[0]->{$key}; + return; +} + +sub STORE { + my ($self, $key, $value) = @_; + my $parent = $self->[1]; + $key = lc($key); + if (not exists $self->[0]->{$key}) { + push @{$parent->{in_order}}, field_capitalize($key); + } + $self->[0]->{$key} = $value; +} + +sub EXISTS { + my ($self, $key) = @_; + $key = lc($key); + return exists $self->[0]->{$key}; +} + +sub DELETE { + my ($self, $key) = @_; + my $parent = $self->[1]; + my $in_order = $parent->{in_order}; + $key = lc($key); + if (exists $self->[0]->{$key}) { + delete $self->[0]->{$key}; + @$in_order = grep { lc($_) ne $key } @$in_order; + return 1; + } else { + return 0; + } +} + +sub FIRSTKEY { + my $self = shift; + my $parent = $self->[1]; + foreach (@{$parent->{in_order}}) { + return $_ if exists $self->[0]->{lc($_)}; + } +} + +sub NEXTKEY { + my ($self, $last) = @_; + my $parent = $self->[1]; + my $found = 0; + foreach (@{$parent->{in_order}}) { + if ($found) { + return $_ if exists $self->[0]->{lc($_)}; + } else { + $found = 1 if $_ eq $last; + } + } + return; +} + +1; + +=back + +=head1 CHANGES + +=head2 Version 1.01 + +New method: parse_error(). + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Control/Info.pm b/third_party/dpkg-dev/scripts/Dpkg/Control/Info.pm new file mode 100644 index 0000000..17c5b892 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Control/Info.pm
@@ -0,0 +1,209 @@ +# Copyright © 2007-2010 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Control::Info; + +use strict; +use warnings; + +our $VERSION = '1.00'; + +use Dpkg::Control; +use Dpkg::ErrorHandling; +use Dpkg::Gettext; + +use parent qw(Dpkg::Interface::Storable); + +use overload + '@{}' => sub { return [ $_[0]->{source}, @{$_[0]->{packages}} ] }; + +=encoding utf8 + +=head1 NAME + +Dpkg::Control::Info - parse files like debian/control + +=head1 DESCRIPTION + +It provides an object to access data of files that follow the same +syntax as F<debian/control>. + +=head1 FUNCTIONS + +=over 4 + +=item $c = Dpkg::Control::Info->new($file) + +Create a new Dpkg::Control::Info object for $file. If $file is omitted, it +loads F<debian/control>. If file is "-", it parses the standard input. + +=cut + +sub new { + my ($this, $arg) = @_; + my $class = ref($this) || $this; + my $self = { + source => undef, + packages => [], + }; + bless $self, $class; + if ($arg) { + $self->load($arg); + } else { + $self->load('debian/control'); + } + return $self; +} + +=item $c->reset() + +Resets what got read. + +=cut + +sub reset { + my $self = shift; + $self->{source} = undef; + $self->{packages} = []; +} + +=item $c->load($file) + +Load the content of $file. Exits in case of errors. If file is "-", it +loads from the standard input. + +=item $c->parse($fh, $description) + +Parse a control file from the given filehandle. Exits in case of errors. +$description is used to describe the filehandle, ideally it's a filename +or a description of where the data comes from. It's used in error +messages. + +=cut + +sub parse { + my ($self, $fh, $desc) = @_; + $self->reset(); + my $cdata = Dpkg::Control->new(type => CTRL_INFO_SRC); + return if not $cdata->parse($fh, $desc); + $self->{source} = $cdata; + unless (exists $cdata->{Source}) { + $cdata->parse_error($desc, _g('first block lacks a source field')); + } + while (1) { + $cdata = Dpkg::Control->new(type => CTRL_INFO_PKG); + last if not $cdata->parse($fh, $desc); + push @{$self->{packages}}, $cdata; + unless (exists $cdata->{Package}) { + $cdata->parse_error($desc, _g("block lacks the '%s' field"), + 'Package'); + } + unless (exists $cdata->{Architecture}) { + $cdata->parse_error($desc, _g("block lacks the '%s' field"), + 'Architecture'); + } + + } +} + +=item $c->[0] + +=item $c->get_source() + +Returns a Dpkg::Control object containing the fields concerning the +source package. + +=cut + +sub get_source { + my $self = shift; + return $self->{source}; +} + +=item $c->get_pkg_by_idx($idx) + +Returns a Dpkg::Control object containing the fields concerning the binary +package numbered $idx (starting at 1). + +=cut + +sub get_pkg_by_idx { + my ($self, $idx) = @_; + return $self->{packages}[--$idx]; +} + +=item $c->get_pkg_by_name($name) + +Returns a Dpkg::Control object containing the fields concerning the binary +package named $name. + +=cut + +sub get_pkg_by_name { + my ($self, $name) = @_; + foreach my $pkg (@{$self->{packages}}) { + return $pkg if ($pkg->{Package} eq $name); + } + return; +} + + +=item $c->get_packages() + +Returns a list containing the Dpkg::Control objects for all binary packages. + +=cut + +sub get_packages { + my $self = shift; + return @{$self->{packages}}; +} + +=item $c->output($filehandle) + +Dump the content into a filehandle. + +=cut + +sub output { + my ($self, $fh) = @_; + my $str; + $str .= $self->{source}->output($fh); + foreach my $pkg (@{$self->{packages}}) { + print { $fh } "\n"; + $str .= "\n" . $pkg->output($fh); + } + return $str; +} + +=item "$c" + +Return a string representation of the content. + +=item @{$c} + +Return a list of Dpkg::Control objects, the first one is corresponding to +source information and the following ones are the binary packages +information. + +=back + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Control/Types.pm b/third_party/dpkg-dev/scripts/Dpkg/Control/Types.pm new file mode 100644 index 0000000..d8bc0b3 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Control/Types.pm
@@ -0,0 +1,62 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Control::Types; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use Exporter qw(import); +our @EXPORT = qw(CTRL_UNKNOWN CTRL_INFO_SRC CTRL_INFO_PKG CTRL_INDEX_SRC + CTRL_INDEX_PKG CTRL_PKG_SRC CTRL_PKG_DEB CTRL_FILE_CHANGES + CTRL_FILE_VENDOR CTRL_FILE_STATUS CTRL_CHANGELOG); + +=encoding utf8 + +=head1 NAME + +Dpkg::Control::Types - export CTRL_* constants + +=head1 DESCRIPTION + +You should not use this module directly. Instead you more likely +want to use Dpkg::Control which also re-exports the same constants. + +This module has been introduced solely to avoid a dependency loop +between Dpkg::Control and Dpkg::Control::Fields. + +=cut + +use constant { + CTRL_UNKNOWN => 0, + CTRL_INFO_SRC => 1, # First control block in debian/control + CTRL_INFO_PKG => 2, # Subsequent control blocks in debian/control + CTRL_INDEX_SRC => 4, # Entry in repository's Packages files + CTRL_INDEX_PKG => 8, # Entry in repository's Sources files + CTRL_PKG_SRC => 16, # .dsc file of source package + CTRL_PKG_DEB => 32, # DEBIAN/control in binary packages + CTRL_FILE_CHANGES => 64, # .changes file + CTRL_FILE_VENDOR => 128, # File in $Dpkg::CONFDIR/origins + CTRL_FILE_STATUS => 256, # $Dpkg::ADMINDIR/status + CTRL_CHANGELOG => 512, # Output of dpkg-parsechangelog +}; + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Deps.pm b/third_party/dpkg-dev/scripts/Dpkg/Deps.pm new file mode 100644 index 0000000..7bc18153 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Deps.pm
@@ -0,0 +1,1468 @@ +# Copyright © 2007-2009 Raphaël Hertzog <hertzog@debian.org> +# Copyright © 2012 Guillem Jover <guillem@debian.org> +# +# This program is free software; you may redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +######################################################################### +# Several parts are inspired by lib/Dep.pm from lintian (same license) +# +# Copyright © 1998 Richard Braakman +# Portions Copyright © 1999 Darren Benham +# Portions Copyright © 2000 Sean 'Shaleh' Perry +# Portions Copyright © 2004 Frank Lichtenheld +# Portions Copyright © 2006 Russ Allbery + +package Dpkg::Deps; + +=encoding utf8 + +=head1 NAME + +Dpkg::Deps - parse and manipulate dependencies of Debian packages + +=head1 DESCRIPTION + +The Dpkg::Deps module provides objects implementing various types of +dependencies. + +The most important function is deps_parse(), it turns a dependency line in +a set of Dpkg::Deps::{Simple,AND,OR,Union} objects depending on the case. + +=head1 FUNCTIONS + +All the deps_* functions are exported by default. + +=over 4 + +=cut + +use strict; +use warnings; + +our $VERSION = '1.02'; + +use Dpkg::Version; +use Dpkg::Arch qw(get_host_arch get_build_arch); +use Dpkg::BuildProfiles qw(get_build_profiles); +use Dpkg::ErrorHandling; +use Dpkg::Gettext; + +use Exporter qw(import); +our @EXPORT = qw(deps_concat deps_parse deps_eval_implication deps_compare); + +=item deps_eval_implication($rel_p, $v_p, $rel_q, $v_q) + +($rel_p, $v_p) and ($rel_q, $v_q) express two dependencies as (relation, +version). The relation variable can have the following values that are +exported by Dpkg::Version: REL_EQ, REL_LT, REL_LE, REL_GT, REL_GT. + +This functions returns 1 if the "p" dependency implies the "q" +dependency. It returns 0 if the "p" dependency implies that "q" is +not satisfied. It returns undef when there's no implication. + +The $v_p and $v_q parameter should be Dpkg::Version objects. + +=cut + +sub deps_eval_implication { + my ($rel_p, $v_p, $rel_q, $v_q) = @_; + + # If versions are not valid, we can't decide of any implication + return unless defined($v_p) and $v_p->is_valid(); + return unless defined($v_q) and $v_q->is_valid(); + + # q wants an exact version, so p must provide that exact version. p + # disproves q if q's version is outside the range enforced by p. + if ($rel_q eq REL_EQ) { + if ($rel_p eq REL_LT) { + return ($v_p <= $v_q) ? 0 : undef; + } elsif ($rel_p eq REL_LE) { + return ($v_p < $v_q) ? 0 : undef; + } elsif ($rel_p eq REL_GT) { + return ($v_p >= $v_q) ? 0 : undef; + } elsif ($rel_p eq REL_GE) { + return ($v_p > $v_q) ? 0 : undef; + } elsif ($rel_p eq REL_EQ) { + return ($v_p == $v_q); + } + } + + # A greater than clause may disprove a less than clause. An equal + # cause might as well. Otherwise, if + # p's clause is <<, <=, or =, the version must be <= q's to imply q. + if ($rel_q eq REL_LE) { + if ($rel_p eq REL_GT) { + return ($v_p >= $v_q) ? 0 : undef; + } elsif ($rel_p eq REL_GE) { + return ($v_p > $v_q) ? 0 : undef; + } elsif ($rel_p eq REL_EQ) { + return ($v_p <= $v_q) ? 1 : 0; + } else { # <<, <= + return ($v_p <= $v_q) ? 1 : undef; + } + } + + # Similar, but << is stronger than <= so p's version must be << q's + # version if the p relation is <= or =. + if ($rel_q eq REL_LT) { + if ($rel_p eq REL_GT or $rel_p eq REL_GE) { + return ($v_p >= $v_p) ? 0 : undef; + } elsif ($rel_p eq REL_LT) { + return ($v_p <= $v_q) ? 1 : undef; + } elsif ($rel_p eq REL_EQ) { + return ($v_p < $v_q) ? 1 : 0; + } else { # <<, <= + return ($v_p < $v_q) ? 1 : undef; + } + } + + # Same logic as above, only inverted. + if ($rel_q eq REL_GE) { + if ($rel_p eq REL_LT) { + return ($v_p <= $v_q) ? 0 : undef; + } elsif ($rel_p eq REL_LE) { + return ($v_p < $v_q) ? 0 : undef; + } elsif ($rel_p eq REL_EQ) { + return ($v_p >= $v_q) ? 1 : 0; + } else { # >>, >= + return ($v_p >= $v_q) ? 1 : undef; + } + } + if ($rel_q eq REL_GT) { + if ($rel_p eq REL_LT or $rel_p eq REL_LE) { + return ($v_p <= $v_q) ? 0 : undef; + } elsif ($rel_p eq REL_GT) { + return ($v_p >= $v_q) ? 1 : undef; + } elsif ($rel_p eq REL_EQ) { + return ($v_p > $v_q) ? 1 : 0; + } else { + return ($v_p > $v_q) ? 1 : undef; + } + } + + return; +} + +=item my $dep = deps_concat(@dep_list) + +This function concatenates multiple dependency lines into a single line, +joining them with ", " if appropriate, and always returning a valid string. + +=cut + +sub deps_concat { + my (@dep_list) = @_; + + return join(', ', grep { defined $_ } @dep_list); +} + +=item my $dep = deps_parse($line, %options) + +This function parses the dependency line and returns an object, either a +Dpkg::Deps::AND or a Dpkg::Deps::Union. Various options can alter the +behaviour of that function. + +=over 4 + +=item use_arch (defaults to 1) + +Take into account the architecture restriction part of the dependencies. +Set to 0 to completely ignore that information. + +=item host_arch (defaults to the current architecture) + +Define the host architecture. By default it uses +Dpkg::Arch::get_host_arch() to identify the proper architecture. + +=item build_arch (defaults to the current architecture) + +Define the build architecture. By default it uses +Dpkg::Arch::get_build_arch() to identify the proper architecture. + +=item reduce_arch (defaults to 0) + +If set to 1, ignore dependencies that do not concern the current host +architecture. This implicitely strips off the architecture restriction +list so that the resulting dependencies are directly applicable to the +current architecture. + +=item use_profiles (defaults to 1) + +Take into account the profile restriction part of the dependencies. Set +to 0 to completely ignore that information. + +=item build_profiles (defaults to no profile) + +Define the active build profiles. By default no profile is defined. + +=item reduce_profiles (defaults to 0) + +If set to 1, ignore dependencies that do not concern the current build +profile. This implicitly strips off the profile restriction list so +that the resulting dependencies are directly applicable to the current +profiles. + +=item reduce_restrictions (defaults to 0) + +If set to 1, ignore dependencies that do not concern the current set of +restrictions. This implicitly strips off any restriction list so that the +resulting dependencies are directly applicable to the current restriction. +This currently implies C<reduce_arch> and C<reduce_profiles>, and overrides +them if set. + +=item union (defaults to 0) + +If set to 1, returns a Dpkg::Deps::Union instead of a Dpkg::Deps::AND. Use +this when parsing non-dependency fields like Conflicts. + +=item build_dep (defaults to 0) + +If set to 1, allow build-dep only arch qualifiers, that is “:native”. +This should be set whenever working with build-deps. + +=back + +=cut + +sub deps_parse { + my $dep_line = shift; + my %options = (@_); + $options{use_arch} = 1 if not exists $options{use_arch}; + $options{reduce_arch} = 0 if not exists $options{reduce_arch}; + $options{host_arch} = get_host_arch() if not exists $options{host_arch}; + $options{build_arch} = get_build_arch() if not exists $options{build_arch}; + $options{use_profiles} = 1 if not exists $options{use_profiles}; + $options{reduce_profiles} = 0 if not exists $options{reduce_profiles}; + $options{build_profiles} = [ get_build_profiles() ] + if not exists $options{build_profiles}; + $options{reduce_restrictions} = 0 if not exists $options{reduce_restrictions}; + $options{union} = 0 if not exists $options{union}; + $options{build_dep} = 0 if not exists $options{build_dep}; + + if ($options{reduce_restrictions}) { + $options{reduce_arch} = 1; + $options{reduce_profiles} = 1; + } + + # Strip trailing/leading spaces + $dep_line =~ s/^\s+//; + $dep_line =~ s/\s+$//; + + my @dep_list; + foreach my $dep_and (split(/\s*,\s*/m, $dep_line)) { + my @or_list = (); + foreach my $dep_or (split(/\s*\|\s*/m, $dep_and)) { + my $dep_simple = Dpkg::Deps::Simple->new($dep_or, host_arch => + $options{host_arch}, + build_arch => + $options{build_arch}, + build_dep => + $options{build_dep}); + if (not defined $dep_simple->{package}) { + warning(_g("can't parse dependency %s"), $dep_or); + return; + } + $dep_simple->{arches} = undef if not $options{use_arch}; + if ($options{reduce_arch}) { + $dep_simple->reduce_arch($options{host_arch}); + next if not $dep_simple->arch_is_concerned($options{host_arch}); + } + $dep_simple->{restrictions} = undef if not $options{use_profiles}; + if ($options{reduce_profiles}) { + $dep_simple->reduce_profiles($options{build_profiles}); + next if not $dep_simple->profile_is_concerned($options{build_profiles}); + } + push @or_list, $dep_simple; + } + next if not @or_list; + if (scalar @or_list == 1) { + push @dep_list, $or_list[0]; + } else { + my $dep_or = Dpkg::Deps::OR->new(); + $dep_or->add($_) foreach (@or_list); + push @dep_list, $dep_or; + } + } + my $dep_and; + if ($options{union}) { + $dep_and = Dpkg::Deps::Union->new(); + } else { + $dep_and = Dpkg::Deps::AND->new(); + } + foreach my $dep (@dep_list) { + if ($options{union} and not $dep->isa('Dpkg::Deps::Simple')) { + warning(_g('an union dependency can only contain simple dependencies')); + return; + } + $dep_and->add($dep); + } + return $dep_and; +} + +=item deps_compare($a, $b) + +Implements a comparison operator between two dependency objects. +This function is mainly used to implement the sort() method. + +=back + +=cut + +my %relation_ordering = ( + undef => 0, + REL_GE() => 1, + REL_GT() => 2, + REL_EQ() => 3, + REL_LT() => 4, + REL_LE() => 5, +); + +sub deps_compare { + my ($a, $b) = @_; + return -1 if $a->is_empty(); + return 1 if $b->is_empty(); + while ($a->isa('Dpkg::Deps::Multiple')) { + return -1 if $a->is_empty(); + my @deps = $a->get_deps(); + $a = $deps[0]; + } + while ($b->isa('Dpkg::Deps::Multiple')) { + return 1 if $b->is_empty(); + my @deps = $b->get_deps(); + $b = $deps[0]; + } + my $ar = defined($a->{relation}) ? $a->{relation} : 'undef'; + my $br = defined($b->{relation}) ? $b->{relation} : 'undef'; + return (($a->{package} cmp $b->{package}) || + ($relation_ordering{$ar} <=> $relation_ordering{$br}) || + ($a->{version} cmp $b->{version})); +} + + +package Dpkg::Deps::Simple; + +=head1 OBJECTS - Dpkg::Deps::* + +There are several kind of dependencies. A Dpkg::Deps::Simple dependency +represents a single dependency statement (it relates to one package only). +Dpkg::Deps::Multiple dependencies are built on top of this object +and combine several dependencies in a different manners. Dpkg::Deps::AND +represents the logical "AND" between dependencies while Dpkg::Deps::OR +represents the logical "OR". Dpkg::Deps::Multiple objects can contain +Dpkg::Deps::Simple object as well as other Dpkg::Deps::Multiple objects. + +In practice, the code is only meant to handle the realistic cases which, +given Debian's dependencies structure, imply those restrictions: AND can +contain Simple or OR objects, OR can only contain Simple objects. + +Dpkg::Deps::KnownFacts is a special object that is used while evaluating +dependencies and while trying to simplify them. It represents a set of +installed packages along with the virtual packages that they might +provide. + +=head2 COMMON FUNCTIONS + +=over 4 + +=item $dep->is_empty() + +Returns true if the dependency is empty and doesn't contain any useful +information. This is true when a Dpkg::Deps::Simple object has not yet +been initialized or when a (descendant of) Dpkg::Deps::Multiple contains +an empty list of dependencies. + +=item $dep->get_deps() + +Returns a list of sub-dependencies. For Dpkg::Deps::Simple it returns +itself. + +=item $dep->output([$fh]) + +=item "$dep" + +Returns a string representing the dependency. If $fh is set, it prints +the string to the filehandle. + +=item $dep->implies($other_dep) + +Returns 1 when $dep implies $other_dep. Returns 0 when $dep implies +NOT($other_dep). Returns undef when there's no implication. $dep and +$other_dep do not need to be of the same type. + +=item $dep->sort() + +Sorts alphabetically the internal list of dependencies. It's a no-op for +Dpkg::Deps::Simple objects. + +=item $dep->arch_is_concerned($arch) + +Returns true if the dependency applies to the indicated architecture. For +multiple dependencies, it returns true if at least one of the +sub-dependencies apply to this architecture. + +=item $dep->reduce_arch($arch) + +Simplifies the dependency to contain only information relevant to the given +architecture. A Dpkg::Deps::Simple object can be left empty after this +operation. For Dpkg::Deps::Multiple objects, the non-relevant +sub-dependencies are simply removed. + +This trims off the architecture restriction list of Dpkg::Deps::Simple +objects. + +=item $dep->get_evaluation($facts) + +Evaluates the dependency given a list of installed packages and a list of +virtual packages provided. Those lists are part of the +Dpkg::Deps::KnownFacts object given as parameters. + +Returns 1 when it's true, 0 when it's false, undef when some information +is lacking to conclude. + +=item $dep->simplify_deps($facts, @assumed_deps) + +Simplifies the dependency as much as possible given the list of facts (see +object Dpkg::Deps::KnownFacts) and a list of other dependencies that are +known to be true. + +=item $dep->has_arch_restriction() + +For a simple dependency, returns the package name if the dependency +applies only to a subset of architectures. For multiple dependencies, it +returns the list of package names that have such a restriction. + +=item $dep->reset() + +Clears any dependency information stored in $dep so that $dep->is_empty() +returns true. + +=back + +=head2 Dpkg::Deps::Simple + +Such an object has four interesting properties: + +=over 4 + +=item package + +The package name (can be undef if the dependency has not been initialized +or if the simplification of the dependency lead to its removal). + +=item relation + +The relational operator: "=", "<<", "<=", ">=" or ">>". It can be +undefined if the dependency had no version restriction. In that case the +following field is also undefined. + +=item version + +The version. + +=item arches + +The list of architectures where this dependency is applicable. It's +undefined when there's no restriction, otherwise it's an +array ref. It can contain an exclusion list, in that case each +architecture is prefixed with an exclamation mark. + +=item archqual + +The arch qualifier of the dependency (can be undef if there's none). +In the dependency "python:any (>= 2.6)", the arch qualifier is "any". + +=back + +=head3 METHODS + +=over 4 + +=item $simple_dep->parse_string('dpkg-dev (>= 1.14.8) [!hurd-i386]') + +Parses the dependency and modifies internal properties to match the parsed +dependency. + +=item $simple_dep->merge_union($other_dep) + +Returns true if $simple_dep could be modified to represent the union of +both dependencies. Otherwise returns false. + +=back + +=cut + +use strict; +use warnings; + +use Carp; + +use Dpkg::Arch qw(debarch_is); +use Dpkg::Version; +use Dpkg::ErrorHandling; +use Dpkg::Gettext; +use Dpkg::Util qw(:list); + +use parent qw(Dpkg::Interface::Storable); + +sub new { + my ($this, $arg, %opts) = @_; + my $class = ref($this) || $this; + my $self = {}; + bless $self, $class; + $self->reset(); + $self->{host_arch} = $opts{host_arch} || Dpkg::Arch::get_host_arch(); + $self->{build_arch} = $opts{build_arch} || Dpkg::Arch::get_build_arch(); + $self->{build_dep} = $opts{build_dep} || 0; + $self->parse_string($arg) if defined($arg); + return $self; +} + +sub reset { + my ($self) = @_; + $self->{package} = undef; + $self->{relation} = undef; + $self->{version} = undef; + $self->{arches} = undef; + $self->{archqual} = undef; + $self->{restrictions} = undef; +} + +sub parse { + my ($self, $fh, $desc) = @_; + my $line = <$fh>; + chomp($line); + return $self->parse_string($line); +} + +sub parse_string { + my ($self, $dep) = @_; + return if not $dep =~ + m{^\s* # skip leading whitespace + ([a-zA-Z0-9][a-zA-Z0-9+.-]*) # package name + (?: # start of optional part + : # colon for architecture + ([a-zA-Z0-9][a-zA-Z0-9-]*) # architecture name + )? # end of optional part + (?: # start of optional part + \s* \( # open parenthesis for version part + \s* (<<|<=|=|>=|>>|<|>) # relation part + \s* (.*?) # do not attempt to parse version + \s* \) # closing parenthesis + )? # end of optional part + (?: # start of optional architecture + \s* \[ # open bracket for architecture + \s* (.*?) # don't parse architectures now + \s* \] # closing bracket + )? # end of optional architecture + (?: # start of optional restriction + \s* < # open bracket for restriction + \s* (.*?) # don't parse restrictions now + \s* > # closing bracket + )? # end of optional restriction + \s*$ # trailing spaces at end + }x; + if (defined($2)) { + return if $2 eq 'native' and not $self->{build_dep}; + $self->{archqual} = $2; + } + $self->{package} = $1; + $self->{relation} = version_normalize_relation($3) if defined($3); + if (defined($4)) { + $self->{version} = Dpkg::Version->new($4); + } + if (defined($5)) { + $self->{arches} = [ split(/\s+/, $5) ]; + } + if (defined($6)) { + $self->{restrictions} = [ map { lc } split /\s+/, $6 ]; + } +} + +sub output { + my ($self, $fh) = @_; + my $res = $self->{package}; + if (defined($self->{archqual})) { + $res .= ':' . $self->{archqual}; + } + if (defined($self->{relation})) { + $res .= ' (' . $self->{relation} . ' ' . $self->{version} . ')'; + } + if (defined($self->{arches})) { + $res .= ' [' . join(' ', @{$self->{arches}}) . ']'; + } + if (defined($self->{restrictions})) { + $res .= ' <' . join(' ', @{$self->{restrictions}}) . '>'; + } + if (defined($fh)) { + print { $fh } $res; + } + return $res; +} + +# _arch_is_superset(\@p, \@q) +# +# Returns true if the arch list @p is a superset of arch list @q. +# The arguments can also be undef in case there's no explicit architecture +# restriction. +sub _arch_is_superset { + my ($p, $q) = @_; + my $p_arch_neg = defined($p) && $p->[0] =~ /^!/; + my $q_arch_neg = defined($q) && $q->[0] =~ /^!/; + + # If "p" has no arches, it is a superset of q and we should fall through + # to the version check. + if (not defined $p) { + return 1; + } + + # If q has no arches, it is a superset of p and there are no useful + # implications. + elsif (not defined $q) { + return 0; + } + + # Both have arches. If neither are negated, we know nothing useful + # unless q is a subset of p. + elsif (not $p_arch_neg and not $q_arch_neg) { + my %p_arches = map { $_ => 1 } @{$p}; + my $subset = 1; + for my $arch (@{$q}) { + $subset = 0 unless $p_arches{$arch}; + } + return 0 unless $subset; + } + + # If both are negated, we know nothing useful unless p is a subset of + # q (and therefore has fewer things excluded, and therefore is more + # general). + elsif ($p_arch_neg and $q_arch_neg) { + my %q_arches = map { $_ => 1 } @{$q}; + my $subset = 1; + for my $arch (@{$p}) { + $subset = 0 unless $q_arches{$arch}; + } + return 0 unless $subset; + } + + # If q is negated and p isn't, we'd need to know the full list of + # arches to know if there's any relationship, so bail. + elsif (not $p_arch_neg and $q_arch_neg) { + return 0; + } + + # If p is negated and q isn't, q is a subset of p if none of the + # negated arches in p are present in q. + elsif ($p_arch_neg and not $q_arch_neg) { + my %q_arches = map { $_ => 1 } @{$q}; + my $subset = 1; + for my $arch (@{$p}) { + $subset = 0 if $q_arches{substr($arch, 1)}; + } + return 0 unless $subset; + } + return 1; +} + +# _arch_qualifier_allows_implication($p, $q) +# +# Returns true if the arch qualifier $p and $q are compatible with the +# implication $p -> $q, false otherwise. $p/$q can be +# undef/"any"/"native" or an architecture string. +sub _arch_qualifier_allows_implication { + my ($p, $q) = @_; + if (defined $p and $p eq 'any') { + return 1 if defined $q and $q eq 'any'; + return 0; + } elsif (defined $p and $p eq 'native') { + return 1 if defined $q and ($q eq 'any' or $q eq 'native'); + return 0; + } elsif (defined $p) { + return 1 if defined $q and ($p eq $q or $q eq 'any'); + return 0; + } else { + return 0 if defined $q and $q ne 'any' and $q ne 'native'; + return 1; + } +} + +# Returns true if the dependency in parameter can deduced from the current +# dependency. Returns false if it can be negated. Returns undef if nothing +# can be concluded. +sub implies { + my ($self, $o) = @_; + if ($o->isa('Dpkg::Deps::Simple')) { + # An implication is only possible on the same package + return if $self->{package} ne $o->{package}; + + # Our architecture set must be a superset of the architectures for + # o, otherwise we can't conclude anything. + return unless _arch_is_superset($self->{arches}, $o->{arches}); + + # The arch qualifier must not forbid an implication + return unless _arch_qualifier_allows_implication($self->{archqual}, + $o->{archqual}); + + # If o has no version clause, then our dependency is stronger + return 1 if not defined $o->{relation}; + # If o has a version clause, we must also have one, otherwise there + # can't be an implication + return if not defined $self->{relation}; + + return Dpkg::Deps::deps_eval_implication($self->{relation}, + $self->{version}, $o->{relation}, $o->{version}); + + } elsif ($o->isa('Dpkg::Deps::AND')) { + # TRUE: Need to imply all individual elements + # FALSE: Need to NOT imply at least one individual element + my $res = 1; + foreach my $dep ($o->get_deps()) { + my $implication = $self->implies($dep); + unless (defined($implication) && $implication == 1) { + $res = $implication; + last if defined $res; + } + } + return $res; + } elsif ($o->isa('Dpkg::Deps::OR')) { + # TRUE: Need to imply at least one individual element + # FALSE: Need to not apply all individual elements + # UNDEF: The rest + my $res = undef; + foreach my $dep ($o->get_deps()) { + my $implication = $self->implies($dep); + if (defined($implication)) { + if (not defined $res) { + $res = $implication; + } else { + if ($implication) { + $res = 1; + } else { + $res = 0; + } + } + last if defined($res) && $res == 1; + } + } + return $res; + } else { + croak 'Dpkg::Deps::Simple cannot evaluate implication with a ' . + ref($o); + } +} + +sub get_deps { + my $self = shift; + return $self; +} + +sub sort { + # Nothing to sort +} + +sub arch_is_concerned { + my ($self, $host_arch) = @_; + + return 0 if not defined $self->{package}; # Empty dep + return 1 if not defined $self->{arches}; # Dep without arch spec + + my $seen_arch = 0; + foreach my $arch (@{$self->{arches}}) { + $arch=lc($arch); + + if ($arch =~ /^!/) { + my $not_arch = $arch; + $not_arch =~ s/^!//; + + if (debarch_is($host_arch, $not_arch)) { + $seen_arch = 0; + last; + } else { + # !arch includes by default all other arches + # unless they also appear in a !otherarch + $seen_arch = 1; + } + } elsif (debarch_is($host_arch, $arch)) { + $seen_arch = 1; + last; + } + } + return $seen_arch; +} + +sub reduce_arch { + my ($self, $host_arch) = @_; + if (not $self->arch_is_concerned($host_arch)) { + $self->reset(); + } else { + $self->{arches} = undef; + } +} + +sub has_arch_restriction { + my ($self) = @_; + if (defined $self->{arches}) { + return $self->{package}; + } else { + return (); + } +} + +sub profile_is_concerned { + my ($self, $build_profiles) = @_; + + return 0 if not defined $self->{package}; # Empty dep + return 1 if not defined $self->{restrictions}; # Dep without restrictions + + my $seen_profile = 0; + foreach my $restriction (@{$self->{restrictions}}) { + # Determine if this restriction is negated, and within the "profile" + # namespace, otherwise it does not concern this check. + next if $restriction !~ m/^(!)?profile\.(.*)/; + + my $negated = defined $1 && $1 eq '!'; + my $profile = $2; + + # Determine if the restriction matches any of the specified profiles. + my $found = any { $_ eq $profile } @{$build_profiles}; + + if ($negated) { + if ($found) { + $seen_profile = 0; + last; + } else { + # "!profile.this" includes by default all other profiles + # unless they also appear in a "!profile.other". + $seen_profile = 1; + } + } elsif ($found) { + $seen_profile = 1; + last; + } + } + return $seen_profile; +} + +sub reduce_profiles { + my ($self, $build_profiles) = @_; + + if (not $self->profile_is_concerned($build_profiles)) { + $self->reset(); + } else { + $self->{restrictions} = undef; + } +} + +sub get_evaluation { + my ($self, $facts) = @_; + return if not defined $self->{package}; + return $facts->_evaluate_simple_dep($self); +} + +sub simplify_deps { + my ($self, $facts) = @_; + my $eval = $self->get_evaluation($facts); + $self->reset() if defined $eval and $eval == 1; +} + +sub is_empty { + my $self = shift; + return not defined $self->{package}; +} + +sub merge_union { + my ($self, $o) = @_; + return 0 if not $o->isa('Dpkg::Deps::Simple'); + return 0 if $self->is_empty() or $o->is_empty(); + return 0 if $self->{package} ne $o->{package}; + return 0 if defined $self->{arches} or defined $o->{arches}; + + if (not defined $o->{relation} and defined $self->{relation}) { + # Union is the non-versioned dependency + $self->{relation} = undef; + $self->{version} = undef; + return 1; + } + + my $implication = $self->implies($o); + my $rev_implication = $o->implies($self); + if (defined($implication)) { + if ($implication) { + $self->{relation} = $o->{relation}; + $self->{version} = $o->{version}; + return 1; + } else { + return 0; + } + } + if (defined($rev_implication)) { + if ($rev_implication) { + # Already merged... + return 1; + } else { + return 0; + } + } + return 0; +} + +package Dpkg::Deps::Multiple; + +=head2 Dpkg::Deps::Multiple + +This is the base class for Dpkg::Deps::{AND,OR,Union}. It implements +the following methods: + +=over 4 + +=item $mul->add($dep) + +Adds a new dependency object at the end of the list. + +=back + +=cut + +use strict; +use warnings; + +use Carp; + +use Dpkg::ErrorHandling; + +use parent qw(Dpkg::Interface::Storable); + +sub new { + my $this = shift; + my $class = ref($this) || $this; + my $self = { list => [ @_ ] }; + bless $self, $class; + return $self; +} + +sub reset { + my ($self) = @_; + $self->{list} = []; +} + +sub add { + my $self = shift; + push @{$self->{list}}, @_; +} + +sub get_deps { + my $self = shift; + return grep { not $_->is_empty() } @{$self->{list}}; +} + +sub sort { + my $self = shift; + my @res = (); + @res = sort { Dpkg::Deps::deps_compare($a, $b) } @{$self->{list}}; + $self->{list} = [ @res ]; +} + +sub arch_is_concerned { + my ($self, $host_arch) = @_; + my $res = 0; + foreach my $dep (@{$self->{list}}) { + $res = 1 if $dep->arch_is_concerned($host_arch); + } + return $res; +} + +sub reduce_arch { + my ($self, $host_arch) = @_; + my @new; + foreach my $dep (@{$self->{list}}) { + $dep->reduce_arch($host_arch); + push @new, $dep if $dep->arch_is_concerned($host_arch); + } + $self->{list} = [ @new ]; +} + +sub has_arch_restriction { + my ($self) = @_; + my @res; + foreach my $dep (@{$self->{list}}) { + push @res, $dep->has_arch_restriction(); + } + return @res; +} + + +sub is_empty { + my $self = shift; + return scalar @{$self->{list}} == 0; +} + +sub merge_union { + croak 'method merge_union() is only valid for Dpkg::Deps::Simple'; +} + +package Dpkg::Deps::AND; + +=head2 Dpkg::Deps::AND + +This object represents a list of dependencies who must be met at the same +time. + +=over 4 + +=item $and->output([$fh]) + +The output method uses ", " to join the list of sub-dependencies. + +=back + +=cut + +use strict; +use warnings; + +use parent -norequire, qw(Dpkg::Deps::Multiple); + +sub output { + my ($self, $fh) = @_; + my $res = join(', ', map { $_->output() } grep { not $_->is_empty() } $self->get_deps()); + if (defined($fh)) { + print { $fh } $res; + } + return $res; +} + +sub implies { + my ($self, $o) = @_; + # If any individual member can imply $o or NOT $o, we're fine + foreach my $dep ($self->get_deps()) { + my $implication = $dep->implies($o); + return 1 if defined($implication) && $implication == 1; + return 0 if defined($implication) && $implication == 0; + } + # If o is an AND, we might have an implication, if we find an + # implication within us for each predicate in o + if ($o->isa('Dpkg::Deps::AND')) { + my $subset = 1; + foreach my $odep ($o->get_deps()) { + my $found = 0; + foreach my $dep ($self->get_deps()) { + $found = 1 if $dep->implies($odep); + } + $subset = 0 if not $found; + } + return 1 if $subset; + } + return; +} + +sub get_evaluation { + my ($self, $facts) = @_; + # Return 1 only if all members evaluates to true + # Return 0 if at least one member evaluates to false + # Return undef otherwise + my $result = 1; + foreach my $dep ($self->get_deps()) { + my $eval = $dep->get_evaluation($facts); + if (not defined $eval) { + $result = undef; + } elsif ($eval == 0) { + $result = 0; + last; + } elsif ($eval == 1) { + # Still possible + } + } + return $result; +} + +sub simplify_deps { + my ($self, $facts, @knowndeps) = @_; + my @new; + +WHILELOOP: + while (@{$self->{list}}) { + my $dep = shift @{$self->{list}}; + my $eval = $dep->get_evaluation($facts); + next if defined($eval) and $eval == 1; + foreach my $odep (@knowndeps, @new) { + next WHILELOOP if $odep->implies($dep); + } + # When a dependency is implied by another dependency that + # follows, then invert them + # "a | b, c, a" becomes "a, c" and not "c, a" + my $i = 0; + foreach my $odep (@{$self->{list}}) { + if (defined $odep and $odep->implies($dep)) { + splice @{$self->{list}}, $i, 1; + unshift @{$self->{list}}, $odep; + next WHILELOOP; + } + $i++; + } + push @new, $dep; + } + $self->{list} = [ @new ]; +} + + +package Dpkg::Deps::OR; + +=head2 Dpkg::Deps::OR + +This object represents a list of dependencies of which only one must be met +for the dependency to be true. + +=over 4 + +=item $or->output([$fh]) + +The output method uses " | " to join the list of sub-dependencies. + +=back + +=cut + +use strict; +use warnings; + +use parent -norequire, qw(Dpkg::Deps::Multiple); + +sub output { + my ($self, $fh) = @_; + my $res = join(' | ', map { $_->output() } grep { not $_->is_empty() } $self->get_deps()); + if (defined($fh)) { + print { $fh } $res; + } + return $res; +} + +sub implies { + my ($self, $o) = @_; + + # Special case for AND with a single member, replace it by its member + if ($o->isa('Dpkg::Deps::AND')) { + my @subdeps = $o->get_deps(); + if (scalar(@subdeps) == 1) { + $o = $subdeps[0]; + } + } + + # In general, an OR dependency can't imply anything except if each + # of its member implies a member in the other OR dependency + if ($o->isa('Dpkg::Deps::OR')) { + my $subset = 1; + foreach my $dep ($self->get_deps()) { + my $found = 0; + foreach my $odep ($o->get_deps()) { + $found = 1 if $dep->implies($odep); + } + $subset = 0 if not $found; + } + return 1 if $subset; + } + return; +} + +sub get_evaluation { + my ($self, $facts) = @_; + # Returns false if all members evaluates to 0 + # Returns true if at least one member evaluates to true + # Returns undef otherwise + my $result = 0; + foreach my $dep ($self->get_deps()) { + my $eval = $dep->get_evaluation($facts); + if (not defined $eval) { + $result = undef; + } elsif ($eval == 1) { + $result = 1; + last; + } elsif ($eval == 0) { + # Still possible to have a false evaluation + } + } + return $result; +} + +sub simplify_deps { + my ($self, $facts) = @_; + my @new; + +WHILELOOP: + while (@{$self->{list}}) { + my $dep = shift @{$self->{list}}; + my $eval = $dep->get_evaluation($facts); + if (defined($eval) and $eval == 1) { + $self->{list} = []; + return; + } + foreach my $odep (@new, @{$self->{list}}) { + next WHILELOOP if $odep->implies($dep); + } + push @new, $dep; + } + $self->{list} = [ @new ]; +} + +package Dpkg::Deps::Union; + +=head2 Dpkg::Deps::Union + +This object represents a list of relationships. + +=over 4 + +=item $union->output([$fh]) + +The output method uses ", " to join the list of relationships. + +=item $union->implies($other_dep) + +=item $union->get_evaluation($other_dep) + +Those methods are not meaningful for this object and always return undef. + +=item $union->simplify_deps($facts) + +The simplication is done to generate an union of all the relationships. +It uses $simple_dep->merge_union($other_dep) to get its job done. + +=back + +=cut + +use strict; +use warnings; + +use parent -norequire, qw(Dpkg::Deps::Multiple); + +sub output { + my ($self, $fh) = @_; + my $res = join(', ', map { $_->output() } grep { not $_->is_empty() } $self->get_deps()); + if (defined($fh)) { + print { $fh } $res; + } + return $res; +} + +sub implies { + # Implication test are not useful on Union + return; +} + +sub get_evaluation { + # Evaluation are not useful on Union + return; +} + +sub simplify_deps { + my ($self, $facts) = @_; + my @new; + +WHILELOOP: + while (@{$self->{list}}) { + my $odep = shift @{$self->{list}}; + foreach my $dep (@new) { + next WHILELOOP if $dep->merge_union($odep); + } + push @new, $odep; + } + $self->{list} = [ @new ]; +} + +package Dpkg::Deps::KnownFacts; + +=head2 Dpkg::Deps::KnownFacts + +This object represents a list of installed packages and a list of virtual +packages provided (by the set of installed packages). + +=over 4 + +=item my $facts = Dpkg::Deps::KnownFacts->new(); + +Creates a new object. + +=cut + +use strict; +use warnings; + +use Dpkg::Version; + +sub new { + my $this = shift; + my $class = ref($this) || $this; + my $self = { + pkg => {}, + virtualpkg => {}, + }; + bless $self, $class; + return $self; +} + +=item $facts->add_installed_package($package, $version, $arch, $multiarch) + +Records that the given version of the package is installed. If +$version/$arch is undefined we know that the package is installed but we +don't know which version/architecture it is. $multiarch is the Multi-Arch +field of the package. If $multiarch is undef, it will be equivalent to +"Multi-Arch: no". + +Note that $multiarch is only used if $arch is provided. + +=cut + +sub add_installed_package { + my ($self, $pkg, $ver, $arch, $multiarch) = @_; + my $p = { + package => $pkg, + version => $ver, + architecture => $arch, + multiarch => $multiarch || 'no', + }; + $self->{pkg}{"$pkg:$arch"} = $p if defined $arch; + push @{$self->{pkg}{$pkg}}, $p; +} + +=item $facts->add_provided_package($virtual, $relation, $version, $by) + +Records that the "$by" package provides the $virtual package. $relation +and $version correspond to the associated relation given in the Provides +field. This might be used in the future for versioned provides. + +=cut + +sub add_provided_package { + my ($self, $pkg, $rel, $ver, $by) = @_; + if (not exists $self->{virtualpkg}{$pkg}) { + $self->{virtualpkg}{$pkg} = []; + } + push @{$self->{virtualpkg}{$pkg}}, [ $by, $rel, $ver ]; +} + +=item my ($check, $param) = $facts->check_package($package) + +$check is one when the package is found. For a real package, $param +contains the version. For a virtual package, $param contains an array +reference containing the list of packages that provide it (each package is +listed as [ $provider, $relation, $version ]). + +This function is obsolete and should not be used. Dpkg::Deps::KnownFacts +is only meant to be filled with data and then passed to Dpkg::Deps +methods where appropriate, but it should not be directly queried. + +=back + +=cut + +sub check_package { + my ($self, $pkg) = @_; + if (exists $self->{pkg}{$pkg}) { + return (1, $self->{pkg}{$pkg}[0]{version}); + } + if (exists $self->{virtualpkg}{$pkg}) { + return (1, $self->{virtualpkg}{$pkg}); + } + return (0, undef); +} + +## The functions below are private to Dpkg::Deps + +sub _find_package { + my ($self, $dep, $lackinfos) = @_; + my ($pkg, $archqual) = ($dep->{package}, $dep->{archqual}); + return if not exists $self->{pkg}{$pkg}; + my $host_arch = $dep->{host_arch}; + my $build_arch = $dep->{build_arch}; + foreach my $p (@{$self->{pkg}{$pkg}}) { + my $a = $p->{architecture}; + my $ma = $p->{multiarch}; + if (not defined $a) { + $$lackinfos = 1; + next; + } + if (not defined $archqual) { + return $p if $ma eq 'foreign'; + return $p if $a eq $host_arch or $a eq 'all'; + } elsif ($archqual eq 'any') { + return $p if $ma eq 'allowed'; + } elsif ($archqual eq 'native') { + return $p if $a eq $build_arch and $ma ne 'foreign'; + } else { + return $p if $a eq $archqual; + } + } + return; +} + +sub _find_virtual_packages { + my ($self, $pkg) = @_; + return () if not exists $self->{virtualpkg}{$pkg}; + return @{$self->{virtualpkg}{$pkg}}; +} + +sub _evaluate_simple_dep { + my ($self, $dep) = @_; + my ($lackinfos, $pkg) = (0, $dep->{package}); + my $p = $self->_find_package($dep, \$lackinfos); + if ($p) { + if (defined $dep->{relation}) { + if (defined $p->{version}) { + return 1 if version_compare_relation($p->{version}, + $dep->{relation}, $dep->{version}); + } else { + $lackinfos = 1; + } + } else { + return 1; + } + } + foreach my $virtpkg ($self->_find_virtual_packages($pkg)) { + # XXX: Adapt when versioned provides are allowed + next if defined $virtpkg->[1]; + next if defined $dep->{relation}; # Provides don't satisfy versioned deps + return 1; + } + return if $lackinfos; + return 0; +} + +=head1 CHANGES + +=head2 Version 1.02 + +=over + +=item * Add new Dpkg::deps_concat() function. + +=back + +=head2 Version 1.01 + +=over + +=item * Add new $dep->reset() method that all dependency objects support. + +=item * Dpkg::Deps::Simple now recognizes the arch qualifier "any" and +stores it in the "archqual" property when present. + +=item * Dpkg::Deps::KnownFacts->add_installed_package() now accepts 2 +supplementary parameters ($arch and $multiarch). + +=item * Dpkg::Deps::KnownFacts->check_package() is obsolete, it should +not have been part of the public API. + +=back + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/ErrorHandling.pm b/third_party/dpkg-dev/scripts/Dpkg/ErrorHandling.pm new file mode 100644 index 0000000..275c506 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/ErrorHandling.pm
@@ -0,0 +1,107 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::ErrorHandling; + +use strict; +use warnings; + +our $VERSION = '0.02'; + +use Dpkg (); +use Dpkg::Gettext; + +use Exporter qw(import); +our @EXPORT = qw(report_options info warning error errormsg + syserr subprocerr usageerr); +our @EXPORT_OK = qw(report); + +my $quiet_warnings = 0; +my $info_fh = \*STDOUT; + +sub report_options +{ + my (%options) = @_; + + if (exists $options{quiet_warnings}) { + $quiet_warnings = $options{quiet_warnings}; + } + if (exists $options{info_fh}) { + $info_fh = $options{info_fh}; + } +} + +sub report(@) +{ + my ($type, $msg) = (shift, shift); + + $msg = sprintf($msg, @_) if (@_); + return "$Dpkg::PROGNAME: $type: $msg\n"; +} + +sub info($;@) +{ + print { $info_fh } report(_g('info'), @_) if (!$quiet_warnings); +} + +sub warning($;@) +{ + warn report(_g('warning'), @_) if (!$quiet_warnings); +} + +sub syserr($;@) +{ + my $msg = shift; + die report(_g('error'), "$msg: $!", @_); +} + +sub error($;@) +{ + die report(_g('error'), @_); +} + +sub errormsg($;@) +{ + print { *STDERR } report(_g('error'), @_); +} + +sub subprocerr(@) +{ + my ($p) = (shift); + + $p = sprintf($p, @_) if (@_); + + require POSIX; + + if (POSIX::WIFEXITED($?)) { + error(_g('%s gave error exit status %s'), $p, POSIX::WEXITSTATUS($?)); + } elsif (POSIX::WIFSIGNALED($?)) { + error(_g('%s died from signal %s'), $p, POSIX::WTERMSIG($?)); + } else { + error(_g('%s failed with unknown exit code %d'), $p, $?); + } +} + +my $printforhelp = _g('Use --help for program usage information.'); + +sub usageerr(@) +{ + my ($msg) = (shift); + + $msg = sprintf($msg, @_) if (@_); + warn "$Dpkg::PROGNAME: $msg\n\n"; + warn "$printforhelp\n"; + exit(2); +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Exit.pm b/third_party/dpkg-dev/scripts/Dpkg/Exit.pm new file mode 100644 index 0000000..b967f42 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Exit.pm
@@ -0,0 +1,98 @@ +# Copyright © 2002 Adam Heath <doogie@debian.org> +# Copyright © 2012-2013 Guillem Jover <guillem@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Exit; + +use strict; +use warnings; + +our $VERSION = '1.01'; + +use Exporter qw(import); + +our @EXPORT_OK = qw(push_exit_handler pop_exit_handler run_exit_handlers); + +# XXX: Backwards compatibility, stop exporting on VERSION 2.00. +## no critic (Variables::ProhibitPackageVars) +our @handlers = (); +## use critic + +=encoding utf8 + +=head1 NAME + +Dpkg::Exit - program exit handlers + +=head1 DESCRIPTION + +The Dpkg::Exit module provides support functions to run handlers on exit. + +=head1 FUNCTIONS + +=over 4 + +=item push_exit_handler($func) + +Register a code reference into the exit function handlers stack. + +=cut + +sub push_exit_handler { + my ($func) = shift; + push @handlers, $func; +} + +=item pop_exit_handler() + +Pop the last registered exit handler from the handlers stack. + +=cut + +sub pop_exit_handler { + pop @handlers; +} + +=item run_exit_handlers() + +Run the registered exit handlers. + +=cut + +sub run_exit_handlers { + &$_() foreach (reverse @handlers); +} + +sub exit_handler { + run_exit_handlers(); + exit(127); +} + +$SIG{INT} = \&exit_handler; +$SIG{HUP} = \&exit_handler; +$SIG{QUIT} = \&exit_handler; + +=back + +=head1 CHANGES + +=head2 Version 1.01 + +New functions: push_exit_handler(), pop_exit_handler(), run_exit_handlers() +Deprecated variable: @handlers + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/File.pm b/third_party/dpkg-dev/scripts/Dpkg/File.pm new file mode 100644 index 0000000..adc46547 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/File.pm
@@ -0,0 +1,60 @@ +# Copyright © 2011 Raphaël Hertzog <hertzog@debian.org> +# Copyright © 2012 Guillem Jover <guillem@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::File; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use Fcntl qw(:flock); +use Dpkg::Gettext; +use Dpkg::ErrorHandling; + +use Exporter qw(import); +our @EXPORT = qw(file_lock file_slurp); + +sub file_lock($$) { + my ($fh, $filename) = @_; + + # A strict dependency on libfile-fcntllock-perl being it an XS module, + # and dpkg-dev indirectly making use of it, makes building new perl + # package which bump the perl ABI impossible as these packages cannot + # be installed alongside. + eval 'use File::FcntlLock'; + if ($@) { + warning(_g('File::FcntlLock not available; using flock which is not NFS-safe')); + flock($fh, LOCK_EX) + or syserr(_g('failed to get a write lock on %s'), $filename); + } else { + eval q{ + my $fs = File::FcntlLock->new(l_type => F_WRLCK); + $fs->lock($fh, F_SETLKW) + or syserr(_g('failed to get a write lock on %s'), $filename); + } + } +} + +sub file_slurp { + my ($fh) = @_; + + local $/; + my $data = <$fh>; + return $data; +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Gettext.pm b/third_party/dpkg-dev/scripts/Dpkg/Gettext.pm new file mode 100644 index 0000000..14f9f5c --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Gettext.pm
@@ -0,0 +1,66 @@ +# Copied from /usr/share/perl5/Debconf/Gettext.pm +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY AUTHORS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +package Dpkg::Gettext; + +use strict; +use warnings; + +our $VERSION = '1.00'; + +BEGIN { + eval 'use Locale::gettext'; + if ($@) { + eval q{ + sub _g { + return shift; + } + sub textdomain { + } + sub ngettext { + if ($_[2] == 1) { + return $_[0]; + } else { + return $_[1]; + } + } + sub P_ { + return ngettext(@_); + } + }; + } else { + eval q{ + sub _g { + return gettext(shift); + } + sub P_ { + return ngettext(@_); + } + }; + } +} + +use Exporter qw(import); +our @EXPORT=qw(_g P_ textdomain ngettext); + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/IPC.pm b/third_party/dpkg-dev/scripts/Dpkg/IPC.pm new file mode 100644 index 0000000..324c100 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/IPC.pm
@@ -0,0 +1,375 @@ +# Copyright © 2008-2009 Raphaël Hertzog <hertzog@debian.org> +# Copyright © 2008 Frank Lichtenheld <djpig@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::IPC; + +use strict; +use warnings; + +our $VERSION = '1.00'; + +use Dpkg::ErrorHandling; +use Dpkg::Gettext; + +use Carp; +use Exporter qw(import); +our @EXPORT = qw(spawn wait_child); + +=encoding utf8 + +=head1 NAME + +Dpkg::IPC - helper functions for IPC + +=head1 DESCRIPTION + +Dpkg::IPC offers helper functions to allow you to execute +other programs in an easy, yet flexible way, while hiding +all the gory details of IPC (Inter-Process Communication) +from you. + +=head1 METHODS + +=over 4 + +=item spawn + +Creates a child process and executes another program in it. +The arguments are interpreted as a hash of options, specifying +how to handle the in and output of the program to execute. +Returns the pid of the child process (unless the wait_child +option was given). + +Any error will cause the function to exit with one of the +Dpkg::ErrorHandling functions. + +Options: + +=over 4 + +=item exec + +Can be either a scalar, i.e. the name of the program to be +executed, or an array reference, i.e. the name of the program +plus additional arguments. Note that the program will never be +executed via the shell, so you can't specify additional arguments +in the scalar string and you can't use any shell facilities like +globbing. + +Mandatory Option. + +=item from_file, to_file, error_to_file + +Filename as scalar. Standard input/output/error of the +child process will be redirected to the file specified. + +=item from_handle, to_handle, error_to_handle + +Filehandle. Standard input/output/error of the child process will be +dup'ed from the handle. + +=item from_pipe, to_pipe, error_to_pipe + +Scalar reference or object based on IO::Handle. A pipe will be opened for +each of the two options and either the reading (C<to_pipe> and +C<error_to_pipe>) or the writing end (C<from_pipe>) will be returned in +the referenced scalar. Standard input/output/error of the child process +will be dup'ed to the other ends of the pipes. + +=item from_string, to_string, error_to_string + +Scalar reference. Standard input/output/error of the child +process will be redirected to the string given as reference. Note +that it wouldn't be strictly necessary to use a scalar reference +for C<from_string>, as the string is not modified in any way. This was +chosen only for reasons of symmetry with C<to_string> and +C<error_to_string>. C<to_string> and C<error_to_string> imply the +C<wait_child> option. + +=item wait_child + +Scalar. If containing a true value, wait_child() will be called before +returning. The return value of spawn() will be a true value, not the pid. + +=item nocheck + +Scalar. Option of the wait_child() call. + +=item timeout + +Scalar. Option of the wait_child() call. + +=item chdir + +Scalar. The child process will chdir in the indicated directory before +calling exec. + +=item env + +Hash reference. The child process will populate %ENV with the items of the +hash before calling exec. This allows exporting environment variables. + +=item delete_env + +Array reference. The child process will remove all environment variables +listed in the array before calling exec. + +=back + +=cut + +sub _sanity_check_opts { + my (%opts) = @_; + + croak 'exec parameter is mandatory in spawn()' + unless $opts{exec}; + + my $to = my $error_to = my $from = 0; + foreach (qw(file handle string pipe)) { + $to++ if $opts{"to_$_"}; + $error_to++ if $opts{"error_to_$_"}; + $from++ if $opts{"from_$_"}; + } + croak 'not more than one of to_* parameters is allowed' + if $to > 1; + croak 'not more than one of error_to_* parameters is allowed' + if $error_to > 1; + croak 'not more than one of from_* parameters is allowed' + if $from > 1; + + foreach (qw(to_string error_to_string from_string)) { + if (exists $opts{$_} and + (not ref($opts{$_}) or ref($opts{$_}) ne 'SCALAR')) { + croak "parameter $_ must be a scalar reference"; + } + } + + foreach (qw(to_pipe error_to_pipe from_pipe)) { + if (exists $opts{$_} and + (not ref($opts{$_}) or (ref($opts{$_}) ne 'SCALAR' and + not $opts{$_}->isa('IO::Handle')))) { + croak "parameter $_ must be a scalar reference or " . + 'an IO::Handle object'; + } + } + + if (exists $opts{timeout} and defined($opts{timeout}) and + $opts{timeout} !~ /^\d+$/) { + croak 'parameter timeout must be an integer'; + } + + if (exists $opts{env} and ref($opts{env}) ne 'HASH') { + croak 'parameter env must be a hash reference'; + } + + if (exists $opts{delete_env} and ref($opts{delete_env}) ne 'ARRAY') { + croak 'parameter delete_env must be an array reference'; + } + + return %opts; +} + +sub spawn { + my (%opts) = _sanity_check_opts(@_); + $opts{close_in_child} ||= []; + my @prog; + if (ref($opts{exec}) =~ /ARRAY/) { + push @prog, @{$opts{exec}}; + } elsif (not ref($opts{exec})) { + push @prog, $opts{exec}; + } else { + croak 'invalid exec parameter in spawn()'; + } + my ($from_string_pipe, $to_string_pipe, $error_to_string_pipe); + if ($opts{to_string}) { + $opts{to_pipe} = \$to_string_pipe; + $opts{wait_child} = 1; + } + if ($opts{error_to_string}) { + $opts{error_to_pipe} = \$error_to_string_pipe; + $opts{wait_child} = 1; + } + if ($opts{from_string}) { + $opts{from_pipe} = \$from_string_pipe; + } + # Create pipes if needed + my ($input_pipe, $output_pipe, $error_pipe); + if ($opts{from_pipe}) { + pipe($opts{from_handle}, $input_pipe) + or syserr(_g('pipe for %s'), "@prog"); + ${$opts{from_pipe}} = $input_pipe; + push @{$opts{close_in_child}}, $input_pipe; + } + if ($opts{to_pipe}) { + pipe($output_pipe, $opts{to_handle}) + or syserr(_g('pipe for %s'), "@prog"); + ${$opts{to_pipe}} = $output_pipe; + push @{$opts{close_in_child}}, $output_pipe; + } + if ($opts{error_to_pipe}) { + pipe($error_pipe, $opts{error_to_handle}) + or syserr(_g('pipe for %s'), "@prog"); + ${$opts{error_to_pipe}} = $error_pipe; + push @{$opts{close_in_child}}, $error_pipe; + } + # Fork and exec + my $pid = fork(); + syserr(_g('cannot fork for %s'), "@prog") unless defined $pid; + if (not $pid) { + # Define environment variables + if ($opts{env}) { + foreach (keys %{$opts{env}}) { + $ENV{$_} = $opts{env}{$_}; + } + } + if ($opts{delete_env}) { + delete $ENV{$_} foreach (@{$opts{delete_env}}); + } + # Change the current directory + if ($opts{chdir}) { + chdir($opts{chdir}) or syserr(_g('chdir to %s'), $opts{chdir}); + } + # Redirect STDIN if needed + if ($opts{from_file}) { + open(STDIN, '<', $opts{from_file}) + or syserr(_g('cannot open %s'), $opts{from_file}); + } elsif ($opts{from_handle}) { + open(STDIN, '<&', $opts{from_handle}) + or syserr(_g('reopen stdin')); + close($opts{from_handle}); # has been duped, can be closed + } + # Redirect STDOUT if needed + if ($opts{to_file}) { + open(STDOUT, '>', $opts{to_file}) + or syserr(_g('cannot write %s'), $opts{to_file}); + } elsif ($opts{to_handle}) { + open(STDOUT, '>&', $opts{to_handle}) + or syserr(_g('reopen stdout')); + close($opts{to_handle}); # has been duped, can be closed + } + # Redirect STDERR if needed + if ($opts{error_to_file}) { + open(STDERR, '>', $opts{error_to_file}) + or syserr(_g('cannot write %s'), $opts{error_to_file}); + } elsif ($opts{error_to_handle}) { + open(STDERR, '>&', $opts{error_to_handle}) + or syserr(_g('reopen stdout')); + close($opts{error_to_handle}); # has been duped, can be closed + } + # Close some inherited filehandles + close($_) foreach (@{$opts{close_in_child}}); + # Execute the program + exec({ $prog[0] } @prog) or syserr(_g('unable to execute %s'), "@prog"); + } + # Close handle that we can't use any more + close($opts{from_handle}) if exists $opts{from_handle}; + close($opts{to_handle}) if exists $opts{to_handle}; + close($opts{error_to_handle}) if exists $opts{error_to_handle}; + + if ($opts{from_string}) { + print { $from_string_pipe } ${$opts{from_string}}; + close($from_string_pipe); + } + if ($opts{to_string}) { + local $/ = undef; + ${$opts{to_string}} = readline($to_string_pipe); + } + if ($opts{error_to_string}) { + local $/ = undef; + ${$opts{error_to_string}} = readline($error_to_string_pipe); + } + if ($opts{wait_child}) { + my $cmdline = "@prog"; + if ($opts{env}) { + foreach (keys %{$opts{env}}) { + $cmdline = "$_=\"" . $opts{env}{$_} . "\" $cmdline"; + } + } + wait_child($pid, nocheck => $opts{nocheck}, + timeout => $opts{timeout}, cmdline => $cmdline); + return 1; + } + + return $pid; +} + + +=item wait_child + +Takes as first argument the pid of the process to wait for. +Remaining arguments are taken as a hash of options. Returns +nothing. Fails if the child has been ended by a signal or +if it exited non-zero. + +Options: + +=over 4 + +=item cmdline + +String to identify the child process in error messages. +Defaults to "child process". + +=item nocheck + +If true do not check the return status of the child (and thus +do not fail it it has been killed or if it exited with a +non-zero return code). + +=item timeout + +Set a maximum time to wait for the process, after that fail +with an error message. + +=back + +=cut + +sub wait_child { + my ($pid, %opts) = @_; + $opts{cmdline} ||= _g('child process'); + croak 'no PID set, cannot wait end of process' unless $pid; + eval { + local $SIG{ALRM} = sub { die "alarm\n" }; + alarm($opts{timeout}) if defined($opts{timeout}); + $pid == waitpid($pid, 0) or syserr(_g('wait for %s'), $opts{cmdline}); + alarm(0) if defined($opts{timeout}); + }; + if ($@) { + die $@ unless $@ eq "alarm\n"; + error(ngettext("%s didn't complete in %d second", + "%s didn't complete in %d seconds", + $opts{timeout}), + $opts{cmdline}, $opts{timeout}); + } + unless ($opts{nocheck}) { + subprocerr($opts{cmdline}) if $?; + } +} + +1; +__END__ + +=back + +=head1 AUTHORS + +Written by Raphaël Hertzog <hertzog@debian.org> and +Frank Lichtenheld <djpig@debian.org>. + +=head1 SEE ALSO + +Dpkg, Dpkg::ErrorHandling
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Index.pm b/third_party/dpkg-dev/scripts/Dpkg/Index.pm new file mode 100644 index 0000000..057d9101 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Index.pm
@@ -0,0 +1,353 @@ +# Copyright © 2009 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Index; + +use strict; +use warnings; + +our $VERSION = '1.00'; + +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Control; +use Dpkg::Compression::FileHandle; + +use parent qw(Dpkg::Interface::Storable); + +use overload + '@{}' => sub { return $_[0]->{order} }, + fallback => 1; + +=encoding utf8 + +=head1 NAME + +Dpkg::Index - generic index of control information + +=head1 DESCRIPTION + +This object represent a set of Dpkg::Control objects. + +=head1 FUNCTIONS + +=over 4 + +=item my $index = Dpkg::Index->new(%opts) + +Creates a new empty index. See set_options() for more details. + +=cut + +sub new { + my ($this, %opts) = @_; + my $class = ref($this) || $this; + + my $self = { + items => {}, + order => [], + get_key_func => sub { return $_[0]->{Package} }, + type => CTRL_UNKNOWN, + }; + bless $self, $class; + $self->set_options(%opts); + if (exists $opts{load}) { + $self->load($opts{load}); + } + + return $self; +} + +=item $index->set_options(%opts) + +The "type" option is checked first to define default values for other +options. Here are the relevant options: "get_key_func" is a function +returning a key for the item passed in parameters. The index can only +contain one item with a given key. The function used depend on the +type: for CTRL_INFO_PKG, CTRL_INDEX_SRC, CTRL_INDEX_PKG and CTRL_PKG_DEB +it's simply the Package field; for CTRL_PKG_SRC and CTRL_INFO_SRC, it's +the Source field; for CTRL_CHANGELOG it's the Source and the Version +fields (concatenated with an intermediary "_"); for CTRL_FILE_CHANGES it's +the Source, Version and Architecture fields (concatenated with "_"); +for CTRL_FILE_VENDOR it's the Vendor field; for CTRL_FILE_STATUS it's the +Package and Architecture fields (concatenated with "_"). Otherwise it's +the Package field by default. + +=cut + +sub set_options { + my ($self, %opts) = @_; + + # Default values based on type + if (exists $opts{type}) { + my $t = $opts{type}; + if ($t == CTRL_INFO_PKG or $t == CTRL_INDEX_SRC or + $t == CTRL_INDEX_PKG or $t == CTRL_PKG_DEB) { + $self->{get_key_func} = sub { return $_[0]->{Package}; }; + } elsif ($t == CTRL_PKG_SRC or $t == CTRL_INFO_SRC) { + $self->{get_key_func} = sub { return $_[0]->{Source}; }; + } elsif ($t == CTRL_CHANGELOG) { + $self->{get_key_func} = sub { + return $_[0]->{Source} . '_' . $_[0]->{Version}; + }; + } elsif ($t == CTRL_FILE_CHANGES) { + $self->{get_key_func} = sub { + return $_[0]->{Source} . '_' . $_[0]->{Version} . '_' . + $_[0]->{Architecture}; + }; + } elsif ($t == CTRL_FILE_VENDOR) { + $self->{get_key_func} = sub { return $_[0]->{Vendor}; }; + } elsif ($t == CTRL_FILE_STATUS) { + $self->{get_key_func} = sub { + return $_[0]->{Package} . '_' . $_[0]->{Architecture}; + }; + } + } + + # Options set by the user override default values + $self->{$_} = $opts{$_} foreach keys %opts; +} + +=item $index->get_type() + +Returns the type of control information stored. See the type parameter +set during new(). + +=cut + +sub get_type { + my ($self) = @_; + return $self->{type}; +} + +=item $index->add($item, [$key]) + +Add a new item in the index. If the $key parameter is omitted, the key +will be generated with the get_key_func function (see set_options() for +details). + +=cut + +sub add { + my ($self, $item, $key) = @_; + unless (defined $key) { + $key = $self->{get_key_func}($item); + } + if (not exists $self->{items}{$key}) { + push @{$self->{order}}, $key; + } + $self->{items}{$key} = $item; +} + +=item $index->load($file) + +Reads the file and creates all items parsed. Returns the number of items +parsed. Handles compressed files transparently based on their extensions. + +=item $index->parse($fh, $desc) + +Reads the filehandle and creates all items parsed. Returns the number of +items parsed. + +=cut + +sub parse { + my ($self, $fh, $desc) = @_; + my $item = $self->new_item(); + my $i = 0; + while ($item->parse($fh, $desc)) { + $self->add($item); + $item = $self->new_item(); + $i++; + } + return $i; +} + +=item $index->save($file) + +Writes the content of the index in a file. Auto-compresses files +based on their extensions. + +=item my $item = $index->new_item() + +Creates a new item. Mainly useful for derived objects that would want +to override this method to return something else than a Dpkg::Control +object. + +=cut + +sub new_item { + my ($self) = @_; + return Dpkg::Control->new(type => $self->{type}); +} + +=item my $item = $index->get_by_key($key) + +Returns the item identified by $key or undef. + +=cut + +sub get_by_key { + my ($self, $key) = @_; + return $self->{items}{$key} if exists $self->{items}{$key}; + return; +} + +=item my @keys = $index->get_keys(%criteria) + +Returns the keys of items that matches all the criteria. The key of the +%criteria hash is a field name and the value is either a regex that needs +to match the field value, or a reference to a function that must return +true and that receives the field value as single parameter, or a scalar +that must be equal to the field value. + +=cut + +sub get_keys { + my ($self, %crit) = @_; + my @selected = @{$self->{order}}; + foreach my $s_crit (keys %crit) { # search criteria + if (ref($crit{$s_crit}) eq 'Regexp') { + @selected = grep { + $self->{items}{$_}{$s_crit} =~ $crit{$s_crit} + } @selected; + } elsif (ref($crit{$s_crit}) eq 'CODE') { + @selected = grep { + &{$crit{$s_crit}}($self->{items}{$_}{$s_crit}); + } @selected; + } else { + @selected = grep { + $self->{items}{$_}{$s_crit} eq $crit{$s_crit} + } @selected; + } + } + return @selected; +} + +=item my @items = $index->get(%criteria) + +Returns all the items that matches all the criteria. + +=cut + +sub get { + my ($self, %crit) = @_; + return map { $self->{items}{$_} } $self->get_keys(%crit); +} + +=item $index->remove_by_key($key) + +Remove the item identified by the given key. + +=cut + +sub remove_by_key { + my ($self, $key) = @_; + @{$self->{order}} = grep { $_ ne $key } @{$self->{order}}; + return delete $self->{items}{$key}; +} + +=item my @items = $index->remove(%criteria) + +Returns and removes all the items that matches all the criteria. + +=cut + +sub remove { + my ($self, %crit) = @_; + my @keys = $self->get_keys(%crit); + my (%keys, @ret); + foreach my $key (@keys) { + $keys{$key} = 1; + push @ret, $self->{items}{$key} if defined wantarray; + delete $self->{items}{$key}; + } + @{$self->{order}} = grep { not exists $keys{$_} } @{$self->{order}}; + return @ret; +} + +=item $index->merge($other_index, %opts) + +Merge the entries of the other index. While merging, the keys of the merged +index are used, they are not re-computed (unless you have set the options +"keep_keys" to "0"). It's your responsibility to ensure that they have been +computed with the same function. + +=cut + +sub merge { + my ($self, $other, %opts) = @_; + $opts{keep_keys} = 1 unless exists $opts{keep_keys}; + foreach my $key ($other->get_keys()) { + $self->add($other->get_by_key($key), $opts{keep_keys} ? $key : undef); + } +} + +=item $index->sort(\&sortfunc) + +Sort the index with the given sort function. If no function is given, an +alphabetic sort is done based on the keys. The sort function receives the +items themselves as parameters and not the keys. + +=cut + +sub sort { + my ($self, $func) = @_; + if (defined $func) { + @{$self->{order}} = sort { + &$func($self->{items}{$a}, $self->{items}{$b}) + } @{$self->{order}}; + } else { + @{$self->{order}} = sort @{$self->{order}}; + } +} + +=item my $str = $index->output() + +=item "$index" + +Get a string representation of the index. The Dpkg::Control objects are +output in the order which they have been read or added except if the order +hae been changed with sort(). + +=item $index->output($fh) + +Print the string representation of the index to a filehandle. + +=cut + +sub output { + my ($self, $fh) = @_; + my $str = ''; + foreach my $key ($self->get_keys()) { + if (defined $fh) { + print { $fh } $self->get_by_key($key) . "\n"; + } + if (defined wantarray) { + $str .= $self->get_by_key($key) . "\n"; + } + } + return $str; +} + +=back + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Interface/Storable.pm b/third_party/dpkg-dev/scripts/Dpkg/Interface/Storable.pm new file mode 100644 index 0000000..adef210 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Interface/Storable.pm
@@ -0,0 +1,147 @@ +# Copyright © 2010 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Interface::Storable; + +use strict; +use warnings; + +our $VERSION = '1.00'; + +use Carp; + +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Compression::FileHandle; + +use overload + '""' => \&_stringify, + 'fallback' => 1; + +=encoding utf8 + +=head1 NAME + +Dpkg::Interface::Storable - common methods related to object serialization + +=head1 DESCRIPTION + +Dpkg::Interface::Storable is only meant to be used as parent +class for other objects. It provides common methods that are +all implemented on top of two basic methods parse() and output(). + +=head1 BASE METHODS + +Those methods must be provided by the object that wish to inherit +from Dpkg::Interface::Storable so that the methods provided can work. + +=over 4 + +=item $obj->parse($fh, $desc) + +This methods initialize the object with the data stored in the +filehandle. $desc is optional and is a textual description of +the filehandle used in error messages. + +=item $string = $obj->output($fh) + +This method returns a string representation of the object in $string +and it writes the same string to $fh (if it's defined). + +=back + +=head1 PROVIDED METHODS + +=over 4 + +=item $obj->load($filename) + +Initialize the object with the data stored in the file. The file can be +compressed, it will be uncompressed on the fly by using a +Dpkg::Compression::FileHandle object. If $filename is "-", then the +standard input is read (no compression is allowed in that case). + +=cut + +sub load { + my ($self, $file, @options) = @_; + unless ($self->can('parse')) { + croak ref($self) . ' cannot be loaded, it lacks the parse method'; + } + my ($desc, $fh) = ($file, undef); + if ($file eq '-') { + $fh = \*STDIN; + $desc = _g('<standard input>'); + } else { + $fh = Dpkg::Compression::FileHandle->new(); + open($fh, '<', $file) or syserr(_g('cannot read %s'), $file); + } + my $res = $self->parse($fh, $desc, @options); + if ($file ne '-') { + close($fh) or syserr(_g('cannot close %s'), $file); + } + return $res; +} + +=item $obj->save($filename) + +Store the object in the file. If the filename ends with a known +compression extension, it will be compressed on the fly by using a +Dpkg::Compression::FileHandle object. If $filename is "-", then the +standard output is used (data are written uncompressed in that case). + +=cut + +sub save { + my ($self, $file, @options) = @_; + unless ($self->can('output')) { + croak ref($self) . ' cannot be saved, it lacks the output method'; + } + my $fh; + if ($file eq '-') { + $fh = \*STDOUT; + } else { + $fh = Dpkg::Compression::FileHandle->new(); + open($fh, '>', $file) or syserr(_g('cannot write %s'), $file); + } + $self->output($fh, @options); + if ($file ne '-') { + close($fh) or syserr(_g('cannot close %s'), $file); + } +} + +=item "$obj" + +Return a string representation of the object. + +=cut + +sub _stringify { + my ($self) = @_; + unless ($self->can('output')) { + croak ref($self) . ' cannot be stringified, it lacks the output method'; + } + return $self->output(); +} + +=back + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Package.pm b/third_party/dpkg-dev/scripts/Dpkg/Package.pm new file mode 100644 index 0000000..04fbb00d --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Package.pm
@@ -0,0 +1,45 @@ +# Copyright © 2006 Frank Lichtenheld <djpig@debian.org> +# Copyright © 2007,2012 Guillem Jover <guillem@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Package; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use Dpkg::Gettext; + +use Exporter qw(import); +our @EXPORT = qw(pkg_name_is_illegal); + +sub pkg_name_is_illegal($) { + my $name = shift || ''; + + if ($name eq '') { + return _g('may not be empty string'); + } + if ($name =~ m/[^-+.0-9a-z]/o) { + return sprintf(_g("character '%s' not allowed"), $&); + } + if ($name !~ m/^[0-9a-z]/o) { + return _g('must start with an alphanumeric character'); + } + + return; +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Path.pm b/third_party/dpkg-dev/scripts/Dpkg/Path.pm new file mode 100644 index 0000000..969b1d6c --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Path.pm
@@ -0,0 +1,295 @@ +# Copyright © 2007-2011 Raphaël Hertzog <hertzog@debian.org> +# Copyright © 2011 Linaro Limited +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Path; + +use strict; +use warnings; + +our $VERSION = '1.02'; + +use Exporter qw(import); +use File::Spec; +use Cwd qw(realpath); + +use Dpkg::Arch qw(get_host_arch debarch_to_debtriplet); +use Dpkg::IPC; + +our @EXPORT_OK = qw(get_pkg_root_dir relative_to_pkg_root + guess_pkg_root_dir check_files_are_the_same + resolve_symlink canonpath find_command + get_control_path find_build_file); + +=encoding utf8 + +=head1 NAME + +Dpkg::Path - some common path handling functions + +=head1 DESCRIPTION + +It provides some functions to handle various path. + +=head1 METHODS + +=over 8 + +=item get_pkg_root_dir($file) + +This function will scan upwards the hierarchy of directory to find out +the directory which contains the "DEBIAN" sub-directory and it will return +its path. This directory is the root directory of a package being built. + +If no DEBIAN subdirectory is found, it will return undef. + +=cut + +sub get_pkg_root_dir($) { + my $file = shift; + $file =~ s{/+$}{}; + $file =~ s{/+[^/]+$}{} if not -d $file; + while ($file) { + return $file if -d "$file/DEBIAN"; + last if $file !~ m{/}; + $file =~ s{/+[^/]+$}{}; + } + return; +} + +=item relative_to_pkg_root($file) + +Returns the filename relative to get_pkg_root_dir($file). + +=cut + +sub relative_to_pkg_root($) { + my $file = shift; + my $pkg_root = get_pkg_root_dir($file); + if (defined $pkg_root) { + $pkg_root .= '/'; + return $file if ($file =~ s/^\Q$pkg_root\E//); + } + return; +} + +=item guess_pkg_root_dir($file) + +This function tries to guess the root directory of the package build tree. +It will first use get_pkg_root_dir(), but it will fallback to a more +imprecise check: namely it will use the parent directory that is a +sub-directory of the debian directory. + +It can still return undef if a file outside of the debian sub-directory is +provided. + +=cut + +sub guess_pkg_root_dir($) { + my $file = shift; + my $root = get_pkg_root_dir($file); + return $root if defined $root; + + $file =~ s{/+$}{}; + $file =~ s{/+[^/]+$}{} if not -d $file; + my $parent = $file; + while ($file) { + $parent =~ s{/+[^/]+$}{}; + last if not -d $parent; + return $file if check_files_are_the_same('debian', $parent); + $file = $parent; + last if $file !~ m{/}; + } + return; +} + +=item check_files_are_the_same($file1, $file2, $resolve_symlink) + +This function verifies that both files are the same by checking that the device +numbers and the inode numbers returned by stat()/lstat() are the same. If +$resolve_symlink is true then stat() is used, otherwise lstat() is used. + +=cut + +sub check_files_are_the_same($$;$) { + my ($file1, $file2, $resolve_symlink) = @_; + return 0 if ((! -e $file1) || (! -e $file2)); + my (@stat1, @stat2); + if ($resolve_symlink) { + @stat1 = stat($file1); + @stat2 = stat($file2); + } else { + @stat1 = lstat($file1); + @stat2 = lstat($file2); + } + my $result = ($stat1[0] == $stat2[0]) && ($stat1[1] == $stat2[1]); + return $result; +} + + +=item canonpath($file) + +This function returns a cleaned path. It simplifies double //, and remove +/./ and /../ intelligently. For /../ it simplifies the path only if the +previous element is not a symlink. Thus it should only be used on real +filenames. + +=cut + +sub canonpath($) { + my $path = shift; + $path = File::Spec->canonpath($path); + my ($v, $dirs, $file) = File::Spec->splitpath($path); + my @dirs = File::Spec->splitdir($dirs); + my @new; + foreach my $d (@dirs) { + if ($d eq '..') { + if (scalar(@new) > 0 and $new[-1] ne '..') { + next if $new[-1] eq ''; # Root directory has no parent + my $parent = File::Spec->catpath($v, + File::Spec->catdir(@new), ''); + if (not -l $parent) { + pop @new; + } else { + push @new, $d; + } + } else { + push @new, $d; + } + } else { + push @new, $d; + } + } + return File::Spec->catpath($v, File::Spec->catdir(@new), $file); +} + +=item $newpath = resolve_symlink($symlink) + +Return the filename of the file pointed by the symlink. The new name is +canonicalized by canonpath(). + +=cut + +sub resolve_symlink($) { + my $symlink = shift; + my $content = readlink($symlink); + return unless defined $content; + if (File::Spec->file_name_is_absolute($content)) { + return canonpath($content); + } else { + my ($link_v, $link_d, $link_f) = File::Spec->splitpath($symlink); + my ($cont_v, $cont_d, $cont_f) = File::Spec->splitpath($content); + my $new = File::Spec->catpath($link_v, $link_d . '/' . $cont_d, $cont_f); + return canonpath($new); + } +} + + +=item my $cmdpath = find_command($command) + +Return the path of the command if available on an absolute or relative +path or on the $PATH, undef otherwise. + +=cut + +sub find_command($) { + my $cmd = shift; + + if ($cmd =~ m{/}) { + return "$cmd" if -x "$cmd"; + } else { + foreach my $dir (split(/:/, $ENV{PATH})) { + return "$dir/$cmd" if -x "$dir/$cmd"; + } + } + return; +} + +=item my $control_file = get_control_path($pkg, $filetype) + +Return the path of the control file of type $filetype for the given +package. + +=item my @control_files = get_control_path($pkg) + +Return the path of all available control files for the given package. + +=cut + +sub get_control_path($;$) { + my ($pkg, $filetype) = @_; + my $control_file; + my @exec = ('dpkg-query', '--control-path', $pkg); + push @exec, $filetype if defined $filetype; + spawn(exec => \@exec, wait_child => 1, to_string => \$control_file); + chomp($control_file); + if (defined $filetype) { + return if $control_file eq ''; + return $control_file; + } + return () if $control_file eq ''; + return split(/\n/, $control_file); +} + +=item my $file = find_build_file($basename) + +Selects the right variant of the given file: the arch-specific variant +("$basename.$arch") has priority over the OS-specific variant +("$basename.$os") which has priority over the default variant +("$basename"). If none of the files exists, then it returns undef. + +=item my @files = find_build_file($basename) + +Return the available variants of the given file. Returns an empty +list if none of the files exists. + +=cut + +sub find_build_file($) { + my $base = shift; + my $host_arch = get_host_arch(); + my ($abi, $host_os, $cpu) = debarch_to_debtriplet($host_arch); + my @files; + foreach my $f ("$base.$host_arch", "$base.$host_os", "$base") { + push @files, $f if -f $f; + } + return @files if wantarray; + return $files[0] if scalar @files; + return; +} + +=back + +=head1 CHANGES + +=head2 Version 1.03 + +New function: find_build_file() + +=head2 Version 1.02 + +New function: get_control_path() + +=head2 Version 1.01 + +New function: find_command() + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Shlibs.pm b/third_party/dpkg-dev/scripts/Dpkg/Shlibs.pm new file mode 100644 index 0000000..d7c399a9 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Shlibs.pm
@@ -0,0 +1,149 @@ +# Copyright © 2007 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Shlibs; + +use strict; +use warnings; + +our $VERSION = '0.02'; + +use Exporter qw(import); +our @EXPORT_OK = qw(add_library_dir get_library_paths reset_library_paths + find_library); + + +use File::Spec; + +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Shlibs::Objdump; +use Dpkg::Util qw(:list); +use Dpkg::Path qw(resolve_symlink canonpath); +use Dpkg::Arch qw(debarch_to_gnutriplet get_build_arch get_host_arch + gnutriplet_to_multiarch debarch_to_multiarch); + +use constant DEFAULT_LIBRARY_PATH => + qw(/lib /usr/lib /lib32 /usr/lib32 /lib64 /usr/lib64 + /emul/ia32-linux/lib /emul/ia32-linux/usr/lib); + +# Adjust set of directories to consider when we're in a situation of a +# cross-build or a build of a cross-compiler +my @crosslibrarypaths; +my ($crossprefix, $multiarch); +# Detect cross compiler builds +if ($ENV{GCC_TARGET}) { + $crossprefix = debarch_to_gnutriplet($ENV{GCC_TARGET}); + $multiarch = debarch_to_multiarch($ENV{GCC_TARGET}); +} +if ($ENV{DEB_TARGET_GNU_TYPE} and + ($ENV{DEB_TARGET_GNU_TYPE} ne $ENV{DEB_BUILD_GNU_TYPE})) +{ + $crossprefix = $ENV{DEB_TARGET_GNU_TYPE}; + $multiarch = gnutriplet_to_multiarch($ENV{DEB_TARGET_GNU_TYPE}); +} +# host for normal cross builds. +if (get_build_arch() ne get_host_arch()) { + $crossprefix = debarch_to_gnutriplet(get_host_arch()); + $multiarch = debarch_to_multiarch(get_host_arch()); +} +# Define list of directories containing crossbuilt libraries +if ($crossprefix) { + push @crosslibrarypaths, "/lib/$multiarch", "/usr/lib/$multiarch", + "/$crossprefix/lib", "/usr/$crossprefix/lib", + "/$crossprefix/lib32", "/usr/$crossprefix/lib32", + "/$crossprefix/lib64", "/usr/$crossprefix/lib64"; +} + +my @librarypaths = (DEFAULT_LIBRARY_PATH, @crosslibrarypaths); + +# XXX: Deprecated. Update library paths with LD_LIBRARY_PATH +if ($ENV{LD_LIBRARY_PATH}) { + foreach my $path (reverse split( /:/, $ENV{LD_LIBRARY_PATH} )) { + $path =~ s{/+$}{}; + add_library_dir($path); + } +} + +# Update library paths with ld.so config +parse_ldso_conf('/etc/ld.so.conf') if -e '/etc/ld.so.conf'; + +my %visited; +sub parse_ldso_conf { + my $file = shift; + open my $fh, '<', $file or syserr(_g('cannot open %s'), $file); + $visited{$file}++; + while (<$fh>) { + next if /^\s*$/; + chomp; + s{/+$}{}; + if (/^include\s+(\S.*\S)\s*$/) { + foreach my $include (glob($1)) { + parse_ldso_conf($include) if -e $include + && !$visited{$include}; + } + } elsif (m{^\s*/}) { + s/^\s+//; + my $libdir = $_; + if (none { $_ eq $libdir } @librarypaths) { + push @librarypaths, $libdir; + } + } + } + close $fh; +} + +sub add_library_dir { + my ($dir) = @_; + unshift @librarypaths, $dir; +} + +sub get_library_paths { + return @librarypaths; +} + +sub reset_library_paths { + @librarypaths = (); +} + +# find_library ($soname, \@rpath, $format, $root) +sub find_library { + my ($lib, $rpath, $format, $root) = @_; + $root //= ''; + $root =~ s{/+$}{}; + my @rpath = @{$rpath}; + foreach my $dir (@rpath, @librarypaths) { + my $checkdir = "$root$dir"; + # If the directory checked is a symlink, check if it doesn't + # resolve to another public directory (which is then the canonical + # directory to use instead of this one). Typical example + # is /usr/lib64 -> /usr/lib on amd64. + if (-l $checkdir) { + my $newdir = resolve_symlink($checkdir); + if (any { "$root$_" eq "$newdir" } (@rpath, @librarypaths)) { + $checkdir = $newdir; + } + } + if (-e "$checkdir/$lib") { + my $libformat = Dpkg::Shlibs::Objdump::get_format("$checkdir/$lib"); + if ($format eq $libformat) { + return canonpath("$checkdir/$lib"); + } + } + } + return; +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Shlibs/Cppfilt.pm b/third_party/dpkg-dev/scripts/Dpkg/Shlibs/Cppfilt.pm new file mode 100644 index 0000000..78beb67 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Shlibs/Cppfilt.pm
@@ -0,0 +1,113 @@ +# Copyright © 2009-2010 Modestas Vainius <modax@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Shlibs::Cppfilt; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use Exporter qw(import); + +use Dpkg::ErrorHandling; +use Dpkg::IPC; +use IO::Handle; + +our @EXPORT = qw(cppfilt_demangle_cpp); +our @EXPORT_OK = qw(cppfilt_demangle); + +# A hash of 'objects' referring to preforked c++filt processes for the distinct +# demangling types. +my %cppfilts; + +sub get_cppfilt { + my $type = shift || 'auto'; + + # Fork c++filt process for demangling $type unless it is forked already. + # Keeping c++filt running improves performance a lot. + my $filt; + if (exists $cppfilts{$type}) { + $filt = $cppfilts{$type}; + } else { + $filt = { from => undef, to => undef, + last_symbol => '', last_result => '' }; + $filt->{pid} = spawn(exec => [ 'c++filt', "--format=$type" ], + from_pipe => \$filt->{from}, + to_pipe => \$filt->{to}); + syserr(_g('unable to execute %s'), 'c++filt') + unless defined $filt->{from}; + $filt->{from}->autoflush(1); + + $cppfilts{$type} = $filt; + } + return $filt; +} + +# Demangle the given $symbol using demangler for the specified $type (defaults +# to 'auto') . Extraneous characters trailing after a mangled name are kept +# intact. If neither whole $symbol nor portion of it could be demangled, undef +# is returned. +sub cppfilt_demangle { + my ($symbol, $type) = @_; + + # Start or get c++filt 'object' for the requested type. + my $filt = get_cppfilt($type); + + # Remember the last result. Such a local optimization is cheap and useful + # when sequential pattern matching is performed. + if ($filt->{last_symbol} ne $symbol) { + # This write/read operation should not deadlock because c++filt flushes + # output buffer on LF or each invalid character. + print { $filt->{from} } $symbol, "\n"; + my $demangled = readline($filt->{to}); + chop $demangled; + + # If the symbol was not demangled, return undef + $demangled = undef if $symbol eq $demangled; + + # Remember the last result + $filt->{last_symbol} = $symbol; + $filt->{last_result} = $demangled; + } + return $filt->{last_result}; +} + +sub cppfilt_demangle_cpp { + my $symbol = shift; + return cppfilt_demangle($symbol, 'auto'); +} + +sub terminate_cppfilts { + foreach (keys %cppfilts) { + next if not defined $cppfilts{$_}{pid}; + close $cppfilts{$_}{from}; + close $cppfilts{$_}{to}; + wait_child($cppfilts{$_}{pid}, cmdline => 'c++filt', + nocheck => 1, + timeout => 5); + delete $cppfilts{$_}; + } +} + +# Close/terminate running c++filt process(es) +END { + # Make sure exitcode is not changed (by wait_child) + my $exitcode = $?; + terminate_cppfilts(); + $? = $exitcode; +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Shlibs/Objdump.pm b/third_party/dpkg-dev/scripts/Dpkg/Shlibs/Objdump.pm new file mode 100644 index 0000000..ea3af69 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Shlibs/Objdump.pm
@@ -0,0 +1,475 @@ +# Copyright © 2007-2010 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Shlibs::Objdump; + +use strict; +use warnings; + +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Path qw(find_command); +use Dpkg::Arch qw(debarch_to_gnutriplet get_build_arch get_host_arch); +use Dpkg::IPC; + +our $VERSION = '0.01'; + +# Decide which objdump to call +our $OBJDUMP = 'objdump'; +if (get_build_arch() ne get_host_arch()) { + my $od = debarch_to_gnutriplet(get_host_arch()) . '-objdump'; + $OBJDUMP = $od if find_command($od); +} + + +sub new { + my $this = shift; + my $class = ref($this) || $this; + my $self = { objects => {} }; + bless $self, $class; + return $self; +} + +sub add_object { + my ($self, $obj) = @_; + my $id = $obj->get_id; + if ($id) { + $self->{objects}{$id} = $obj; + } + return $id; +} + +sub analyze { + my ($self, $file) = @_; + my $obj = Dpkg::Shlibs::Objdump::Object->new($file); + + return $self->add_object($obj); +} + +sub locate_symbol { + my ($self, $name) = @_; + foreach my $obj (values %{$self->{objects}}) { + my $sym = $obj->get_symbol($name); + if (defined($sym) && $sym->{defined}) { + return $sym; + } + } + return; +} + +sub get_object { + my ($self, $objid) = @_; + if ($self->has_object($objid)) { + return $self->{objects}{$objid}; + } + return; +} + +sub has_object { + my ($self, $objid) = @_; + return exists $self->{objects}{$objid}; +} + +sub is_armhf { + my ($file) = @_; + my ($output, %opts, $pid, $res); + my $hf = 0; + my $sf = 0; + $pid = spawn(exec => [ "readelf", "-h", "--", $file ], + env => { "LC_ALL" => "C" }, + to_pipe => \$output, %opts); + while (<$output>) { + chomp; + if (/0x5000402/) { + $hf = 1; + last; + } + if (/0x5000202/) { + $sf = 1; + last; + } + } + close($output); + wait_child($pid, nocheck => 1); + if ($?) { + subprocerr("readelf"); + } + if(($hf) || ($sf)) { + return $hf; + } + undef $output; + $pid = spawn(exec => [ "readelf", "-A", "--", $file ], + env => { "LC_ALL" => "C" }, + to_pipe => \$output, %opts); + while (<$output>) { + chomp; + if (/Tag_ABI_VFP_args: VFP registers/) { + $hf = 1; + last; + } + } + close($output); + wait_child($pid, nocheck => 1); + if ($?) { + subprocerr("readelf"); + } + return $hf; +} + +{ + my %format; # Cache of result + sub get_format { + my ($file, $objdump) = @_; + + $objdump //= $OBJDUMP; + + if (exists $format{$file}) { + return $format{$file}; + } else { + my ($output, %opts, $pid, $res); + if ($objdump ne 'objdump') { + $opts{error_to_file} = '/dev/null'; + } + $pid = spawn(exec => [ $objdump, '-a', '--', $file ], + env => { LC_ALL => 'C' }, + to_pipe => \$output, %opts); + while (<$output>) { + chomp; + if (/^\s*\S+:\s*file\s+format\s+(\S+)\s*$/) { + $format{$file} = $1; + $res = $format{$file}; + last; + } + } + close($output); + wait_child($pid, nocheck => 1); + if ($?) { + subprocerr('objdump') if $objdump eq 'objdump'; + $res = get_format($file, 'objdump'); + } + if ($res eq "elf32-littlearm") { + if (is_armhf($file)) { + $res = "elf32-littlearm-hfabi"; + } else { + $res = "elf32-littlearm-sfabi"; + } + $format{$file} = $res; + } + return $res; + } + } +} + +sub is_elf { + my ($file) = @_; + open(my $file_fh, '<', $file) or syserr(_g('cannot read %s'), $file); + my ($header, $result) = ('', 0); + if (read($file_fh, $header, 4) == 4) { + $result = 1 if ($header =~ /^\177ELF$/); + } + close($file_fh); + return $result; +} + +package Dpkg::Shlibs::Objdump::Object; + +use Dpkg::Gettext; +use Dpkg::ErrorHandling; + +sub new { + my $this = shift; + my $file = shift || ''; + my $class = ref($this) || $this; + my $self = {}; + bless $self, $class; + + $self->reset; + if ($file) { + $self->analyze($file); + } + + return $self; +} + +sub reset { + my ($self) = @_; + + $self->{file} = ''; + $self->{id} = ''; + $self->{SONAME} = ''; + $self->{HASH} = ''; + $self->{GNU_HASH} = ''; + $self->{SONAME} = ''; + $self->{NEEDED} = []; + $self->{RPATH} = []; + $self->{dynsyms} = {}; + $self->{flags} = {}; + $self->{dynrelocs} = {}; + + return $self; +} + + +sub analyze { + my ($self, $file) = @_; + + $file ||= $self->{file}; + return unless $file; + + $self->reset; + $self->{file} = $file; + + local $ENV{LC_ALL} = 'C'; + open(my $objdump, '-|', $OBJDUMP, '-w', '-f', '-p', '-T', '-R', $file) + or syserr(_g('cannot fork for %s'), $OBJDUMP); + my $ret = $self->parse_objdump_output($objdump); + close($objdump); + return $ret; +} + +sub parse_objdump_output { + my ($self, $fh) = @_; + + my $section = 'none'; + while (defined($_ = <$fh>)) { + chomp; + next if /^\s*$/; + + if (/^DYNAMIC SYMBOL TABLE:/) { + $section = 'dynsym'; + next; + } elsif (/^DYNAMIC RELOCATION RECORDS/) { + $section = 'dynreloc'; + $_ = <$fh>; # Skip header + next; + } elsif (/^Dynamic Section:/) { + $section = 'dyninfo'; + next; + } elsif (/^Program Header:/) { + $section = 'header'; + next; + } elsif (/^Version definitions:/) { + $section = 'verdef'; + next; + } elsif (/^Version References:/) { + $section = 'verref'; + next; + } + + if ($section eq 'dynsym') { + $self->parse_dynamic_symbol($_); + } elsif ($section eq 'dynreloc') { + if (/^\S+\s+(\S+)\s+(\S+)\s*$/) { + $self->{dynrelocs}{$2} = $1; + } else { + warning(_g("couldn't parse dynamic relocation record: %s"), $_); + } + } elsif ($section eq 'dyninfo') { + if (/^\s*NEEDED\s+(\S+)/) { + push @{$self->{NEEDED}}, $1; + } elsif (/^\s*SONAME\s+(\S+)/) { + $self->{SONAME} = $1; + } elsif (/^\s*HASH\s+(\S+)/) { + $self->{HASH} = $1; + } elsif (/^\s*GNU_HASH\s+(\S+)/) { + $self->{GNU_HASH} = $1; + } elsif (/^\s*RUNPATH\s+(\S+)/) { + # RUNPATH takes precedence over RPATH but is + # considered after LD_LIBRARY_PATH while RPATH + # is considered before (if RUNPATH is not set). + $self->{RPATH} = [ split (/:/, $1) ]; + } elsif (/^\s*RPATH\s+(\S+)/) { + unless (scalar(@{$self->{RPATH}})) { + $self->{RPATH} = [ split (/:/, $1) ]; + } + } + } elsif ($section eq 'none') { + if (/^\s*.+:\s*file\s+format\s+(\S+)\s*$/) { + $self->{format} = $1; + if (($self->{format} eq "elf32-littlearm") && $self->{file}) { + if (Dpkg::Shlibs::Objdump::is_armhf($self->{file})) { + $self->{format} = "elf32-littlearm-hfabi"; + } else { + $self->{format} = "elf32-littlearm-sfabi"; + } + } + } elsif (/^architecture:\s*\S+,\s*flags\s*\S+:\s*$/) { + # Parse 2 lines of "-f" + # architecture: i386, flags 0x00000112: + # EXEC_P, HAS_SYMS, D_PAGED + # start address 0x08049b50 + $_ = <$fh>; + chomp; + $self->{flags}{$_} = 1 foreach (split(/,\s*/)); + } + } + } + # Update status of dynamic symbols given the relocations that have + # been parsed after the symbols... + $self->apply_relocations(); + + return $section ne 'none'; +} + +# Output format of objdump -w -T +# +# /lib/libc.so.6: file format elf32-i386 +# +# DYNAMIC SYMBOL TABLE: +# 00056ef0 g DF .text 000000db GLIBC_2.2 getwchar +# 00000000 g DO *ABS* 00000000 GCC_3.0 GCC_3.0 +# 00069960 w DF .text 0000001e GLIBC_2.0 bcmp +# 00000000 w D *UND* 00000000 _pthread_cleanup_pop_restore +# 0000b788 g DF .text 0000008e Base .protected xine_close +# 0000b788 g DF .text 0000008e .hidden IA__g_free +# | ||||||| | | | | +# | ||||||| | | Version str (.visibility) + Symbol name +# | ||||||| | Alignment +# | ||||||| Section name (or *UND* for an undefined symbol) +# | ||||||F=Function,f=file,O=object +# | |||||d=debugging,D=dynamic +# | ||||I=Indirect +# | |||W=warning +# | ||C=constructor +# | |w=weak +# | g=global,l=local,!=both global/local +# Size of the symbol +# +# GLIBC_2.2 is the version string associated to the symbol +# (GLIBC_2.2) is the same but the symbol is hidden, a newer version of the +# symbol exist + +sub parse_dynamic_symbol { + my ($self, $line) = @_; + my $vis_re = '(\.protected|\.hidden|\.internal|0x\S+)'; + if ($line =~ /^[0-9a-f]+ (.{7})\s+(\S+)\s+[0-9a-f]+(?:\s+(\S+))?(?:\s+$vis_re)?\s+(\S+)/) { + + my ($flags, $sect, $ver, $vis, $name) = ($1, $2, $3, $4, $5); + + # Special case if version is missing but extra visibility + # attribute replaces it in the match + if (defined($ver) and $ver =~ /^$vis_re$/) { + $vis = $ver; + $ver = ''; + } + + # Cleanup visibility field + $vis =~ s/^\.// if defined($vis); + + my $symbol = { + name => $name, + version => defined($ver) ? $ver : '', + section => $sect, + dynamic => substr($flags, 5, 1) eq 'D', + debug => substr($flags, 5, 1) eq 'd', + type => substr($flags, 6, 1), + weak => substr($flags, 1, 1) eq 'w', + local => substr($flags, 0, 1) eq 'l', + global => substr($flags, 0, 1) eq 'g', + visibility => defined($vis) ? $vis : '', + hidden => '', + defined => $sect ne '*UND*' + }; + + # Handle hidden symbols + if (defined($ver) and $ver =~ /^\((.*)\)$/) { + $ver = $1; + $symbol->{version} = $1; + $symbol->{hidden} = 1; + } + + # Register symbol + $self->add_dynamic_symbol($symbol); + } elsif ($line =~ /^[0-9a-f]+ (.{7})\s+(\S+)\s+[0-9a-f]+/) { + # Same start but no version and no symbol ... just ignore + } elsif ($line =~ /^REG_G\d+\s+/) { + # Ignore some s390-specific output like + # REG_G6 g R *UND* 0000000000000000 #scratch + } else { + warning(_g("couldn't parse dynamic symbol definition: %s"), $line); + } +} + +sub apply_relocations { + my ($self) = @_; + foreach my $sym (values %{$self->{dynsyms}}) { + # We want to mark as undefined symbols those which are currently + # defined but that depend on a copy relocation + next if not $sym->{defined}; + next if not exists $self->{dynrelocs}{$sym->{name}}; + if ($self->{dynrelocs}{$sym->{name}} =~ /^R_.*_COPY$/) { + $sym->{defined} = 0; + } + } +} + +sub add_dynamic_symbol { + my ($self, $symbol) = @_; + $symbol->{objid} = $symbol->{soname} = $self->get_id(); + $symbol->{soname} =~ s{^.*/}{} unless $self->{SONAME}; + if ($symbol->{version}) { + $self->{dynsyms}{$symbol->{name} . '@' . $symbol->{version}} = $symbol; + } else { + $self->{dynsyms}{$symbol->{name} . '@Base'} = $symbol; + } +} + +sub get_id { + my $self = shift; + return $self->{SONAME} || $self->{file}; +} + +sub get_symbol { + my ($self, $name) = @_; + if (exists $self->{dynsyms}{$name}) { + return $self->{dynsyms}{$name}; + } + if ($name !~ /@/) { + if (exists $self->{dynsyms}{$name . '@Base'}) { + return $self->{dynsyms}{$name . '@Base'}; + } + } + return; +} + +sub get_exported_dynamic_symbols { + my ($self) = @_; + return grep { $_->{defined} && $_->{dynamic} && !$_->{local} } + values %{$self->{dynsyms}}; +} + +sub get_undefined_dynamic_symbols { + my ($self) = @_; + return grep { (!$_->{defined}) && $_->{dynamic} } + values %{$self->{dynsyms}}; +} + +sub get_needed_libraries { + my $self = shift; + return @{$self->{NEEDED}}; +} + +sub is_executable { + my $self = shift; + return exists $self->{flags}{EXEC_P} && $self->{flags}{EXEC_P}; +} + +sub is_public_library { + my $self = shift; + return exists $self->{flags}{DYNAMIC} && $self->{flags}{DYNAMIC} + && exists $self->{SONAME} && $self->{SONAME}; +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Shlibs/Symbol.pm b/third_party/dpkg-dev/scripts/Dpkg/Shlibs/Symbol.pm new file mode 100644 index 0000000..c39624e --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Shlibs/Symbol.pm
@@ -0,0 +1,507 @@ +# Copyright © 2007 Raphaël Hertzog <hertzog@debian.org> +# Copyright © 2009-2010 Modestas Vainius <modax@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Shlibs::Symbol; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use Dpkg::Gettext; +use Dpkg::Deps; +use Dpkg::ErrorHandling; +use Dpkg::Util qw(:list); +use Dpkg::Version; +use Storable (); +use Dpkg::Shlibs::Cppfilt; + +# Supported alias types in the order of matching preference +use constant ALIAS_TYPES => qw(c++ symver); + +sub new { + my $this = shift; + my $class = ref($this) || $this; + my %args = @_; + my $self = bless { + symbol => undef, + symbol_templ => undef, + minver => undef, + dep_id => 0, + deprecated => 0, + tags => {}, + tagorder => [], + }, $class; + $self->{$_} = $args{$_} foreach keys %args; + return $self; +} + +# Deep clone +sub clone { + my $self = shift; + my $clone = Storable::dclone($self); + if (@_) { + my %args=@_; + $clone->{$_} = $args{$_} foreach keys %args; + } + return $clone; +} + +sub parse_tagspec { + my ($self, $tagspec) = @_; + + if ($tagspec =~ /^\s*\((.*?)\)(.*)$/ && $1) { + # (tag1=t1 value|tag2|...|tagN=tNp) + # Symbols ()|= cannot appear in the tag names and values + my $tagspec = $1; + my $rest = ($2) ? $2 : ''; + my @tags = split(/\|/, $tagspec); + + # Parse each tag + for my $tag (@tags) { + if ($tag =~ /^(.*)=(.*)$/) { + # Tag with value + $self->add_tag($1, $2); + } else { + # Tag without value + $self->add_tag($tag, undef); + } + } + return $rest; + } + return; +} + +sub parse_symbolspec { + my ($self, $symbolspec, %opts) = @_; + my $symbol; + my $symbol_templ; + my $symbol_quoted; + my $rest; + + if (defined($symbol = $self->parse_tagspec($symbolspec))) { + # (tag1=t1 value|tag2|...|tagN=tNp)"Foo::Bar::foobar()"@Base 1.0 1 + # Symbols ()|= cannot appear in the tag names and values + + # If the tag specification exists symbol name template might be quoted too + if ($symbol =~ /^(['"])/ && $symbol =~ /^($1)(.*?)$1(.*)$/) { + $symbol_quoted = $1; + $symbol_templ = $2; + $symbol = $2; + $rest = $3; + } else { + if ($symbol =~ m/^(\S+)(.*)$/) { + $symbol_templ = $1; + $symbol = $1; + $rest = $2; + } + } + error(_g('symbol name unspecified: %s'), $symbolspec) if (!$symbol); + } else { + # No tag specification. Symbol name is up to the first space + # foobarsymbol@Base 1.0 1 + if ($symbolspec =~ m/^(\S+)(.*)$/) { + $symbol = $1; + $rest = $2; + } else { + return 0; + } + } + $self->{symbol} = $symbol; + $self->{symbol_templ} = $symbol_templ; + $self->{symbol_quoted} = $symbol_quoted if ($symbol_quoted); + + # Now parse "the rest" (minver and dep_id) + if ($rest =~ /^\s(\S+)(?:\s(\d+))?/) { + $self->{minver} = $1; + $self->{dep_id} = defined($2) ? $2 : 0; + } elsif (defined $opts{default_minver}) { + $self->{minver} = $opts{default_minver}; + $self->{dep_id} = 0; + } else { + return 0; + } + return 1; +} + +# A hook for symbol initialization (typically processing of tags). The code +# here may even change symbol name. Called from +# Dpkg::Shlibs::SymbolFile::create_symbol(). +sub initialize { + my $self = shift; + + # Look for tags marking symbol patterns. The pattern may match multiple + # real symbols. + my $type; + if ($self->has_tag('c++')) { + # Raw symbol name is always demangled to the same alias while demangled + # symbol name cannot be reliably converted back to raw symbol name. + # Therefore, we can use hash for mapping. + $type = 'alias-c++'; + } + + # Support old style wildcard syntax. That's basically a symver + # with an optional tag. + if ($self->get_symbolname() =~ /^\*@(.*)$/) { + $self->add_tag('symver') unless $self->has_tag('symver'); + $self->add_tag('optional') unless $self->has_tag('optional'); + $self->{symbol} = $1; + } + + if ($self->has_tag('symver')) { + # Each symbol is matched against its version rather than full + # name@version string. + $type = (defined $type) ? 'generic' : 'alias-symver'; + if ($self->get_symbolname() eq 'Base') { + error(_g("you can't use symver tag to catch unversioned symbols: %s"), + $self->get_symbolspec(1)); + } + } + + # As soon as regex is involved, we need to match each real + # symbol against each pattern (aka 'generic' pattern). + if ($self->has_tag('regex')) { + $type = 'generic'; + # Pre-compile regular expression for better performance. + my $regex = $self->get_symbolname(); + $self->{pattern}{regex} = qr/$regex/; + } + if (defined $type) { + $self->init_pattern($type); + } +} + +sub get_symbolname { + return $_[0]->{symbol}; +} + +sub get_symboltempl { + return $_[0]->{symbol_templ} || $_[0]->{symbol}; +} + +sub set_symbolname { + my ($self, $name, $templ, $quoted) = @_; + unless (defined $name) { + $name = $self->{symbol}; + } + if (!defined $templ && $name =~ /\s/) { + $templ = $name; + } + if (!defined $quoted && defined $templ && $templ =~ /\s/) { + $quoted = '"'; + } + $self->{symbol} = $name; + $self->{symbol_templ} = $templ; + if ($quoted) { + $self->{symbol_quoted} = $quoted; + } else { + delete $self->{symbol_quoted}; + } +} + +sub has_tags { + my $self = shift; + return scalar (@{$self->{tagorder}}); +} + +sub add_tag { + my ($self, $tagname, $tagval) = @_; + if (exists $self->{tags}{$tagname}) { + $self->{tags}{$tagname} = $tagval; + return 0; + } else { + $self->{tags}{$tagname} = $tagval; + push @{$self->{tagorder}}, $tagname; + } + return 1; +} + +sub delete_tag { + my ($self, $tagname) = @_; + if (exists $self->{tags}{$tagname}) { + delete $self->{tags}{$tagname}; + $self->{tagorder} = [ grep { $_ ne $tagname } @{$self->{tagorder}} ]; + return 1; + } + return 0; +} + +sub has_tag { + my ($self, $tag) = @_; + return exists $self->{tags}{$tag}; +} + +sub get_tag_value { + my ($self, $tag) = @_; + return $self->{tags}{$tag}; +} + +# Checks if the symbol is equal to another one (by name and optionally, +# tag sets, versioning info (minver and depid)) +sub equals { + my ($self, $other, %opts) = @_; + $opts{versioning} = 1 unless exists $opts{versioning}; + $opts{tags} = 1 unless exists $opts{tags}; + + return 0 if $self->{symbol} ne $other->{symbol}; + + if ($opts{versioning}) { + return 0 if $self->{minver} ne $other->{minver}; + return 0 if $self->{dep_id} ne $other->{dep_id}; + } + + if ($opts{tags}) { + return 0 if scalar(@{$self->{tagorder}}) != scalar(@{$other->{tagorder}}); + + for my $i (0 .. scalar(@{$self->{tagorder}}) - 1) { + my $tag = $self->{tagorder}->[$i]; + return 0 if $tag ne $other->{tagorder}->[$i]; + if (defined $self->{tags}{$tag} && defined $other->{tags}{$tag}) { + return 0 if $self->{tags}{$tag} ne $other->{tags}{$tag}; + } elsif (defined $self->{tags}{$tag} || defined $other->{tags}{$tag}) { + return 0; + } + } + } + + return 1; +} + + +sub is_optional { + my $self = shift; + return $self->has_tag('optional'); +} + +sub is_arch_specific { + my $self = shift; + return $self->has_tag('arch'); +} + +sub arch_is_concerned { + my ($self, $arch) = @_; + my $arches = $self->{tags}{arch}; + + if (defined $arch && defined $arches) { + my $dep = Dpkg::Deps::Simple->new(); + my @arches = split(/[\s,]+/, $arches); + $dep->{package} = 'dummy'; + $dep->{arches} = \@arches; + return $dep->arch_is_concerned($arch); + } + + return 1; +} + +# Get reference to the pattern the symbol matches (if any) +sub get_pattern { + return $_[0]->{matching_pattern}; +} + +### NOTE: subroutines below require (or initialize) $self to be a pattern ### + +# Initializes this symbol as a pattern of the specified type. +sub init_pattern { + my ($self, $type) = @_; + + $self->{pattern}{type} = $type; + # To be filled with references to symbols matching this pattern. + $self->{pattern}{matches} = []; +} + +# Is this symbol a pattern or not? +sub is_pattern { + return exists $_[0]->{pattern}; +} + +# Get pattern type if this symbol is a pattern. +sub get_pattern_type { + return $_[0]->{pattern}{type} || ''; +} + +# Get (sub)type of the alias pattern. Returns empty string if current +# pattern is not alias. +sub get_alias_type { + return ($_[0]->get_pattern_type() =~ /^alias-(.+)/ && $1) || ''; +} + +# Get a list of symbols matching this pattern if this symbol is a pattern +sub get_pattern_matches { + return @{$_[0]->{pattern}{matches}}; +} + +# Create a new symbol based on the pattern (i.e. $self) +# and add it to the pattern matches list. +sub create_pattern_match { + my $self = shift; + return unless $self->is_pattern(); + + # Leave out 'pattern' subfield while deep-cloning + my $pattern_stuff = $self->{pattern}; + delete $self->{pattern}; + my $newsym = $self->clone(@_); + $self->{pattern} = $pattern_stuff; + + # Clean up symbol name related internal fields + $newsym->set_symbolname(); + + # Set newsym pattern reference, add to pattern matches list + $newsym->{matching_pattern} = $self; + push @{$self->{pattern}{matches}}, $newsym; + return $newsym; +} + +### END of pattern subroutines ### + +# Given a raw symbol name the call returns its alias according to the rules of +# the current pattern ($self). Returns undef if the supplied raw name is not +# transformable to alias. +sub convert_to_alias { + my ($self, $rawname, $type) = @_; + $type = $self->get_alias_type() unless $type; + + if ($type) { + if ($type eq 'symver') { + # In case of symver, alias is symbol version. Extract it from the + # rawname. + return "$1" if ($rawname =~ /\@([^@]+)$/); + } elsif ($rawname =~ /^_Z/ && $type eq 'c++') { + return cppfilt_demangle_cpp($rawname); + } + } + return; +} + +sub get_tagspec { + my ($self) = @_; + if ($self->has_tags()) { + my @tags; + for my $tagname (@{$self->{tagorder}}) { + my $tagval = $self->{tags}{$tagname}; + if (defined $tagval) { + push @tags, $tagname . '=' . $tagval; + } else { + push @tags, $tagname; + } + } + return '(' . join('|', @tags) . ')'; + } + return ''; +} + +sub get_symbolspec { + my $self = shift; + my $template_mode = shift; + my $spec = ''; + $spec .= "#MISSING: $self->{deprecated}#" if $self->{deprecated}; + $spec .= ' '; + if ($template_mode) { + if ($self->has_tags()) { + $spec .= sprintf('%s%3$s%s%3$s', $self->get_tagspec(), + $self->get_symboltempl(), $self->{symbol_quoted} || ''); + } else { + $spec .= $self->get_symboltempl(); + } + } else { + $spec .= $self->get_symbolname(); + } + $spec .= " $self->{minver}"; + $spec .= " $self->{dep_id}" if $self->{dep_id}; + return $spec; +} + +# Sanitize the symbol when it is confirmed to be found in +# the respective library. +sub mark_found_in_library { + my ($self, $minver, $arch) = @_; + + if ($self->{deprecated}) { + # Symbol reappeared somehow + $self->{deprecated} = 0; + $self->{minver} = $minver if (not $self->is_optional()); + } else { + # We assume that the right dependency information is already + # there. + if (version_compare($minver, $self->{minver}) < 0) { + $self->{minver} = $minver; + } + } + # Never remove arch tags from patterns + if (not $self->is_pattern()) { + if (not $self->arch_is_concerned($arch)) { + # Remove arch tag because it is incorrect. + $self->delete_tag('arch'); + } + } +} + +# Sanitize the symbol when it is confirmed to be NOT found in +# the respective library. +# Mark as deprecated those that are no more provided (only if the +# minver is later than the version where the symbol was introduced) +sub mark_not_found_in_library { + my ($self, $minver, $arch) = @_; + + # Ignore symbols from foreign arch + return if not $self->arch_is_concerned($arch); + + if ($self->{deprecated}) { + # Bump deprecated if the symbol is optional so that it + # keeps reappering in the diff while it's missing + $self->{deprecated} = $minver if $self->is_optional(); + } elsif (version_compare($minver, $self->{minver}) > 0) { + $self->{deprecated} = $minver; + } +} + +# Checks if the symbol (or pattern) is legitimate as a real symbol for the +# specified architecture. +sub is_legitimate { + my ($self, $arch) = @_; + return ! $self->{deprecated} && + $self->arch_is_concerned($arch); +} + +# Determine whether a supplied raw symbol name matches against current ($self) +# symbol or pattern. +sub matches_rawname { + my ($self, $rawname) = @_; + my $target = $rawname; + my $ok = 1; + my $do_eq_match = 1; + + if ($self->is_pattern()) { + # Process pattern tags in the order they were specified. + for my $tag (@{$self->{tagorder}}) { + if (any { $tag eq $_ } ALIAS_TYPES) { + $ok = not not ($target = $self->convert_to_alias($target, $tag)); + } elsif ($tag eq 'regex') { + # Symbol name is a regex. Match it against the target + $do_eq_match = 0; + $ok = ($target =~ $self->{pattern}{regex}); + } + last if not $ok; + } + } + + # Equality match by default + if ($ok && $do_eq_match) { + $ok = $target eq $self->get_symbolname(); + } + return $ok; +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Shlibs/SymbolFile.pm b/third_party/dpkg-dev/scripts/Dpkg/Shlibs/SymbolFile.pm new file mode 100644 index 0000000..1287d1e --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Shlibs/SymbolFile.pm
@@ -0,0 +1,652 @@ +# Copyright © 2007 Raphaël Hertzog <hertzog@debian.org> +# Copyright © 2009-2010 Modestas Vainius <modax@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Shlibs::SymbolFile; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Version; +use Dpkg::Control::Fields; +use Dpkg::Shlibs::Symbol; +use Dpkg::Arch qw(get_host_arch); + +use parent qw(Dpkg::Interface::Storable); + +my %blacklist = ( + __bss_end__ => 1, # arm + __bss_end => 1, # arm + _bss_end__ => 1, # arm + __bss_start => 1, # ALL + __bss_start__ => 1, # arm + __data_start => 1, # arm + __do_global_ctors_aux => 1, # ia64 + __do_global_dtors_aux => 1, # ia64 + __do_jv_register_classes => 1, # ia64 + _DYNAMIC => 1, # ALL + _edata => 1, # ALL + _end => 1, # ALL + __end__ => 1, # arm + __exidx_end => 1, # armel + __exidx_start => 1, # armel + _fbss => 1, # mips, mipsel + _fdata => 1, # mips, mipsel + _fini => 1, # ALL + _ftext => 1, # mips, mipsel + _GLOBAL_OFFSET_TABLE_ => 1, # hppa, mips, mipsel + __gmon_start__ => 1, # hppa + __gnu_local_gp => 1, # mips, mipsel + _gp => 1, # mips, mipsel + _init => 1, # ALL + _PROCEDURE_LINKAGE_TABLE_ => 1, # sparc, alpha + _SDA2_BASE_ => 1, # powerpc + _SDA_BASE_ => 1, # powerpc +); + +for my $i (14 .. 31) { + # Many powerpc specific symbols + $blacklist{"_restfpr_$i"} = 1; + $blacklist{"_restfpr_$i\_x"} = 1; + $blacklist{"_restgpr_$i"} = 1; + $blacklist{"_restgpr_$i\_x"} = 1; + $blacklist{"_savefpr_$i"} = 1; + $blacklist{"_savegpr_$i"} = 1; +} + +# Many armel-specific symbols +$blacklist{"__aeabi_$_"} = 1 foreach (qw(cdcmpeq cdcmple cdrcmple cfcmpeq +cfcmple cfrcmple d2f d2iz d2lz d2uiz d2ulz dadd dcmpeq dcmpge dcmpgt +dcmple dcmplt dcmpun ddiv dmul dneg drsub dsub f2d f2iz f2lz f2uiz f2ulz +fadd fcmpeq fcmpge fcmpgt fcmple fcmplt fcmpun fdiv fmul fneg frsub fsub +i2d i2f idiv idivmod l2d l2f lasr lcmp ldivmod llsl llsr lmul ui2d ui2f +uidiv uidivmod ul2d ul2f ulcmp uldivmod unwind_cpp_pr0 unwind_cpp_pr1 +unwind_cpp_pr2 uread4 uread8 uwrite4 uwrite8)); + +sub new { + my $this = shift; + my %opts=@_; + my $class = ref($this) || $this; + my $self = \%opts; + bless $self, $class; + $self->{arch} //= get_host_arch(); + $self->clear(); + if (exists $self->{file}) { + $self->load($self->{file}) if -e $self->{file}; + } + return $self; +} + +sub get_arch { + my ($self) = @_; + return $self->{arch}; +} + +sub clear { + my ($self) = @_; + $self->{objects} = {}; +} + +sub clear_except { + my ($self, @ids) = @_; + my %has; + $has{$_} = 1 foreach (@ids); + foreach my $objid (keys %{$self->{objects}}) { + delete $self->{objects}{$objid} unless exists $has{$objid}; + } +} + +sub get_sonames { + my ($self) = @_; + return keys %{$self->{objects}}; +} + +sub get_symbols { + my ($self, $soname) = @_; + if (defined $soname) { + my $obj = $self->get_object($soname); + return (defined $obj) ? values %{$obj->{syms}} : (); + } else { + my @syms; + foreach my $soname ($self->get_sonames()) { + push @syms, $self->get_symbols($soname); + } + return @syms; + } +} + +sub get_patterns { + my ($self, $soname) = @_; + my @patterns; + if (defined $soname) { + my $obj = $self->get_object($soname); + foreach my $alias (values %{$obj->{patterns}{aliases}}) { + push @patterns, values %$alias; + } + return (@patterns, @{$obj->{patterns}{generic}}); + } else { + foreach my $soname ($self->get_sonames()) { + push @patterns, $self->get_patterns($soname); + } + return @patterns; + } +} + +# Create a symbol from the supplied string specification. +sub create_symbol { + my ($self, $spec, %opts) = @_; + my $symbol = (exists $opts{base}) ? $opts{base} : + Dpkg::Shlibs::Symbol->new(); + + my $ret = $opts{dummy} ? $symbol->parse_symbolspec($spec, default_minver => 0) : + $symbol->parse_symbolspec($spec); + if ($ret) { + $symbol->initialize(arch => $self->get_arch()); + return $symbol; + } + return; +} + +sub add_symbol { + my ($self, $symbol, $soname) = @_; + my $object = $self->get_object($soname); + + if ($symbol->is_pattern()) { + if (my $alias_type = $symbol->get_alias_type()) { + unless (exists $object->{patterns}{aliases}{$alias_type}) { + $object->{patterns}{aliases}{$alias_type} = {}; + } + # Alias hash for matching. + my $aliases = $object->{patterns}{aliases}{$alias_type}; + $aliases->{$symbol->get_symbolname()} = $symbol; + } else { + # Otherwise assume this is a generic sequential pattern. This + # should be always safe. + push @{$object->{patterns}{generic}}, $symbol; + } + return 'pattern'; + } else { + # invalidate the minimum version cache + $object->{minver_cache} = []; + $object->{syms}{$symbol->get_symbolname()} = $symbol; + return 'sym'; + } +} + +sub _new_symbol { + my $base = shift || 'Dpkg::Shlibs::Symbol'; + return (ref $base) ? $base->clone(@_) : $base->new(@_); +} + +# Parameter seen is only used for recursive calls +sub parse { + my ($self, $fh, $file, $seen, $obj_ref, $base_symbol) = @_; + + if (defined($seen)) { + return if exists $seen->{$file}; # Avoid include loops + } else { + $self->{file} = $file; + $seen = {}; + } + $seen->{$file} = 1; + + if (not ref($obj_ref)) { # Init ref to name of current object/lib + $$obj_ref = undef; + } + + while (defined($_ = <$fh>)) { + chomp($_); + + if (/^(?:\s+|#(?:DEPRECATED|MISSING): ([^#]+)#\s*)(.*)/) { + if (not defined ($$obj_ref)) { + error(_g('symbol information must be preceded by a header (file %s, line %s)'), $file, $.); + } + # Symbol specification + my $deprecated = ($1) ? $1 : 0; + my $sym = _new_symbol($base_symbol, deprecated => $deprecated); + if ($self->create_symbol($2, base => $sym)) { + $self->add_symbol($sym, $$obj_ref); + } else { + warning(_g('failed to parse line in %s: %s'), $file, $_); + } + } elsif (/^(\(.*\))?#include\s+"([^"]+)"/) { + my $tagspec = $1; + my $filename = $2; + my $dir = $file; + my $new_base_symbol; + if (defined $tagspec) { + $new_base_symbol = _new_symbol($base_symbol); + $new_base_symbol->parse_tagspec($tagspec); + } + $dir =~ s{[^/]+$}{}; # Strip filename + $self->load("$dir$filename", $seen, $obj_ref, $new_base_symbol); + } elsif (/^#|^$/) { + # Skip possible comments and empty lines + } elsif (/^\|\s*(.*)$/) { + # Alternative dependency template + push @{$self->{objects}{$$obj_ref}{deps}}, "$1"; + } elsif (/^\*\s*([^:]+):\s*(.*\S)\s*$/) { + # Add meta-fields + $self->{objects}{$$obj_ref}{fields}{field_capitalize($1)} = $2; + } elsif (/^(\S+)\s+(.*)$/) { + # New object and dependency template + $$obj_ref = $1; + if (exists $self->{objects}{$$obj_ref}) { + # Update/override infos only + $self->{objects}{$$obj_ref}{deps} = [ "$2" ]; + } else { + # Create a new object + $self->create_object($$obj_ref, "$2"); + } + } else { + warning(_g('failed to parse a line in %s: %s'), $file, $_); + } + } + delete $seen->{$file}; +} + +# Beware: we reuse the data structure of the provided symfile so make +# sure to not modify them after having called this function +sub merge_object_from_symfile { + my ($self, $src, $objid) = @_; + if (not $self->has_object($objid)) { + $self->{objects}{$objid} = $src->get_object($objid); + } else { + warning(_g('tried to merge the same object (%s) twice in a symfile'), $objid); + } +} + +sub output { + my ($self, $fh, %opts) = @_; + $opts{template_mode} = 0 unless exists $opts{template_mode}; + $opts{with_deprecated} = 1 unless exists $opts{with_deprecated}; + $opts{with_pattern_matches} = 0 unless exists $opts{with_pattern_matches}; + my $res = ''; + foreach my $soname (sort $self->get_sonames()) { + my @deps = $self->get_dependencies($soname); + my $dep_first = shift @deps; + $dep_first =~ s/#PACKAGE#/$opts{package}/g if exists $opts{package}; + print { $fh } "$soname $dep_first\n" if defined $fh; + $res .= "$soname $dep_first\n" if defined wantarray; + + foreach my $dep_next (@deps) { + $dep_next =~ s/#PACKAGE#/$opts{package}/g if exists $opts{package}; + print { $fh } "| $dep_next\n" if defined $fh; + $res .= "| $dep_next\n" if defined wantarray; + } + my $f = $self->{objects}{$soname}{fields}; + foreach my $field (sort keys %{$f}) { + my $value = $f->{$field}; + $value =~ s/#PACKAGE#/$opts{package}/g if exists $opts{package}; + print { $fh } "* $field: $value\n" if defined $fh; + $res .= "* $field: $value\n" if defined wantarray; + } + + my @symbols; + if ($opts{template_mode}) { + # Exclude symbols matching a pattern, but include patterns themselves + @symbols = grep { not $_->get_pattern() } $self->get_symbols($soname); + push @symbols, $self->get_patterns($soname); + } else { + @symbols = $self->get_symbols($soname); + } + foreach my $sym (sort { $a->get_symboltempl() cmp + $b->get_symboltempl() } @symbols) { + next if $sym->{deprecated} and not $opts{with_deprecated}; + # Do not dump symbols from foreign arch unless dumping a template. + next if not $opts{template_mode} and + not $sym->arch_is_concerned($self->get_arch()); + # Dump symbol specification. Dump symbol tags only in template mode. + print { $fh } $sym->get_symbolspec($opts{template_mode}), "\n" if defined $fh; + $res .= $sym->get_symbolspec($opts{template_mode}) . "\n" if defined wantarray; + # Dump pattern matches as comments (if requested) + if ($opts{with_pattern_matches} && $sym->is_pattern()) { + for my $match (sort { $a->get_symboltempl() cmp + $b->get_symboltempl() } $sym->get_pattern_matches()) + { + print { $fh } '#MATCH:', $match->get_symbolspec(0), "\n" if defined $fh; + $res .= '#MATCH:' . $match->get_symbolspec(0) . "\n" if defined wantarray; + } + } + } + } + return $res; +} + +# Tries to match a symbol name and/or version against the patterns defined. +# Returns a pattern which matches (if any). +sub find_matching_pattern { + my ($self, $refsym, $sonames, $inc_deprecated) = @_; + $inc_deprecated //= 0; + my $name = (ref $refsym) ? $refsym->get_symbolname() : $refsym; + + my $pattern_ok = sub { + my $p = shift; + return defined $p && ($inc_deprecated || !$p->{deprecated}) && + $p->arch_is_concerned($self->get_arch()); + }; + + foreach my $soname ((ref($sonames) eq 'ARRAY') ? @$sonames : $sonames) { + my $obj = $self->get_object($soname); + my ($type, $pattern); + next unless defined $obj; + + my $all_aliases = $obj->{patterns}{aliases}; + for my $type (Dpkg::Shlibs::Symbol::ALIAS_TYPES) { + if (exists $all_aliases->{$type} && keys(%{$all_aliases->{$type}})) { + my $aliases = $all_aliases->{$type}; + my $converter = $aliases->{(keys %$aliases)[0]}; + if (my $alias = $converter->convert_to_alias($name)) { + if ($alias && exists $aliases->{$alias}) { + $pattern = $aliases->{$alias}; + last if &$pattern_ok($pattern); + $pattern = undef; # otherwise not found yet + } + } + } + } + + # Now try generic patterns and use the first that matches + if (not defined $pattern) { + for my $p (@{$obj->{patterns}{generic}}) { + if (&$pattern_ok($p) && $p->matches_rawname($name)) { + $pattern = $p; + last; + } + } + } + if (defined $pattern) { + return (wantarray) ? + ( symbol => $pattern, soname => $soname ) : $pattern; + } + } + return; +} + +# merge_symbols($object, $minver) +# Needs $Objdump->get_object($soname) as parameter +# Don't merge blacklisted symbols related to the internal (arch-specific) +# machinery +sub merge_symbols { + my ($self, $object, $minver) = @_; + + my $soname = $object->{SONAME}; + error(_g('cannot merge symbols from objects without SONAME')) + unless $soname; + + my %dynsyms; + foreach my $sym ($object->get_exported_dynamic_symbols()) { + my $name = $sym->{name} . '@' . + ($sym->{version} ? $sym->{version} : 'Base'); + my $symobj = $self->lookup_symbol($name, $soname); + if (exists $blacklist{$sym->{name}}) { + next unless (defined $symobj and $symobj->has_tag('ignore-blacklist')); + } + $dynsyms{$name} = $sym; + } + + unless ($self->has_object($soname)) { + $self->create_object($soname, ''); + } + # Scan all symbols provided by the objects + my $obj = $self->get_object($soname); + # invalidate the minimum version cache - it is not sufficient to + # invalidate in add_symbol, since we might change a minimum + # version for a particular symbol without adding it + $obj->{minver_cache} = []; + foreach my $name (keys %dynsyms) { + my $sym; + if ($sym = $self->lookup_symbol($name, $obj, 1)) { + # If the symbol is already listed in the file + $sym->mark_found_in_library($minver, $self->get_arch()); + } else { + # The exact symbol is not present in the file, but it might match a + # pattern. + my $pattern = $self->find_matching_pattern($name, $obj, 1); + if (defined $pattern) { + $pattern->mark_found_in_library($minver, $self->get_arch()); + $sym = $pattern->create_pattern_match(symbol => $name); + } else { + # Symbol without any special info as no pattern matched + $sym = Dpkg::Shlibs::Symbol->new(symbol => $name, + minver => $minver); + } + $self->add_symbol($sym, $obj); + } + } + + # Process all symbols which could not be found in the library. + foreach my $sym ($self->get_symbols($soname)) { + if (not exists $dynsyms{$sym->get_symbolname()}) { + $sym->mark_not_found_in_library($minver, $self->get_arch()); + } + } + + # Deprecate patterns which didn't match anything + for my $pattern (grep { $_->get_pattern_matches() == 0 } + $self->get_patterns($soname)) { + $pattern->mark_not_found_in_library($minver, $self->get_arch()); + } +} + +sub is_empty { + my ($self) = @_; + return scalar(keys %{$self->{objects}}) ? 0 : 1; +} + +sub has_object { + my ($self, $soname) = @_; + return exists $self->{objects}{$soname}; +} + +sub get_object { + my ($self, $soname) = @_; + return ref($soname) ? $soname : $self->{objects}{$soname}; +} + +sub create_object { + my ($self, $soname, @deps) = @_; + $self->{objects}{$soname} = { + syms => {}, + fields => {}, + patterns => { + aliases => {}, + generic => [], + }, + deps => [ @deps ], + minver_cache => [] + }; +} + +sub get_dependency { + my ($self, $soname, $dep_id) = @_; + $dep_id //= 0; + return $self->get_object($soname)->{deps}[$dep_id]; +} + +sub get_smallest_version { + my ($self, $soname, $dep_id) = @_; + $dep_id //= 0; + my $so_object = $self->get_object($soname); + return $so_object->{minver_cache}[$dep_id] if(defined($so_object->{minver_cache}[$dep_id])); + my $minver; + foreach my $sym ($self->get_symbols($so_object)) { + next if $dep_id != $sym->{dep_id}; + $minver //= $sym->{minver}; + if (version_compare($minver, $sym->{minver}) > 0) { + $minver = $sym->{minver}; + } + } + $so_object->{minver_cache}[$dep_id] = $minver; + return $minver; +} + +sub get_dependencies { + my ($self, $soname) = @_; + return @{$self->get_object($soname)->{deps}}; +} + +sub get_field { + my ($self, $soname, $name) = @_; + if (my $obj = $self->get_object($soname)) { + if (exists $obj->{fields}{$name}) { + return $obj->{fields}{$name}; + } + } + return; +} + +# Tries to find a symbol like the $refsym and returns its descriptor. +# $refsym may also be a symbol name. +sub lookup_symbol { + my ($self, $refsym, $sonames, $inc_deprecated) = @_; + $inc_deprecated //= 0; + my $name = (ref $refsym) ? $refsym->get_symbolname() : $refsym; + + foreach my $so ((ref($sonames) eq 'ARRAY') ? @$sonames : $sonames) { + if (my $obj = $self->get_object($so)) { + my $sym = $obj->{syms}{$name}; + if ($sym and ($inc_deprecated or not $sym->{deprecated})) + { + return (wantarray) ? + ( symbol => $sym, soname => $so ) : $sym; + } + } + } + return; +} + +# Tries to find a pattern like the $refpat and returns its descriptor. +# $refpat may also be a pattern spec. +sub lookup_pattern { + my ($self, $refpat, $sonames, $inc_deprecated) = @_; + $inc_deprecated //= 0; + # If $refsym is a string, we need to create a dummy ref symbol. + $refpat = $self->create_symbol($refpat, dummy => 1) if ! ref($refpat); + + if ($refpat && $refpat->is_pattern()) { + foreach my $soname ((ref($sonames) eq 'ARRAY') ? @$sonames : $sonames) { + if (my $obj = $self->get_object($soname)) { + my $pat; + if (my $type = $refpat->get_alias_type()) { + if (exists $obj->{patterns}{aliases}{$type}) { + $pat = $obj->{patterns}{aliases}{$type}{$refpat->get_symbolname()}; + } + } elsif ($refpat->get_pattern_type() eq 'generic') { + for my $p (@{$obj->{patterns}{generic}}) { + if (($inc_deprecated || !$p->{deprecated}) && + $p->equals($refpat, versioning => 0)) + { + $pat = $p; + last; + } + } + } + if ($pat && ($inc_deprecated || !$pat->{deprecated})) { + return (wantarray) ? + (symbol => $pat, soname => $soname) : $pat; + } + } + } + } + return; +} + +# Get symbol object reference either by symbol name or by a reference object. +sub get_symbol_object { + my ($self, $refsym, $soname) = @_; + my $sym = $self->lookup_symbol($refsym, $soname, 1); + if (! defined $sym) { + $sym = $self->lookup_pattern($refsym, $soname, 1); + } + return $sym; +} + +sub get_new_symbols { + my ($self, $ref, %opts) = @_; + my $with_optional = (exists $opts{with_optional}) ? + $opts{with_optional} : 0; + my @res; + foreach my $soname ($self->get_sonames()) { + next if not $ref->has_object($soname); + + # Scan raw symbols first. + foreach my $sym (grep { ($with_optional || ! $_->is_optional()) + && $_->is_legitimate($self->get_arch()) } + $self->get_symbols($soname)) + { + my $refsym = $ref->lookup_symbol($sym, $soname, 1); + my $isnew; + if (defined $refsym) { + # If the symbol exists in the $ref symbol file, it might + # still be new if $refsym is not legitimate. + $isnew = not $refsym->is_legitimate($self->get_arch()); + } else { + # If the symbol does not exist in the $ref symbol file, it does + # not mean that it's new. It might still match a pattern in the + # symbol file. However, due to performance reasons, first check + # if the pattern that the symbol matches (if any) exists in the + # ref symbol file as well. + $isnew = not ( + ($sym->get_pattern() and $ref->lookup_pattern($sym->get_pattern(), $soname, 1)) or + $ref->find_matching_pattern($sym, $soname, 1) + ); + } + push @res, { symbol => $sym, soname => $soname } if $isnew; + } + + # Now scan patterns + foreach my $p (grep { ($with_optional || ! $_->is_optional()) + && $_->is_legitimate($self->get_arch()) } + $self->get_patterns($soname)) + { + my $refpat = $ref->lookup_pattern($p, $soname, 0); + # If reference pattern was not found or it is not legitimate, + # considering current one as new. + if (not defined $refpat or + not $refpat->is_legitimate($self->get_arch())) + { + push @res, { symbol => $p , soname => $soname }; + } + } + } + return @res; +} + +sub get_lost_symbols { + my ($self, $ref, %opts) = @_; + return $ref->get_new_symbols($self, %opts); +} + + +sub get_new_libs { + my ($self, $ref) = @_; + my @res; + foreach my $soname ($self->get_sonames()) { + push @res, $soname if not $ref->get_object($soname); + } + return @res; +} + +sub get_lost_libs { + my ($self, $ref) = @_; + return $ref->get_new_libs($self); +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Source/Archive.pm b/third_party/dpkg-dev/scripts/Dpkg/Source/Archive.pm new file mode 100644 index 0000000..6257702b --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Source/Archive.pm
@@ -0,0 +1,163 @@ +# Copyright © 2008 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Source::Archive; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use Dpkg::Source::Functions qw(erasedir fixperms); +use Dpkg::Gettext; +use Dpkg::IPC; +use Dpkg::ErrorHandling; + +use Carp; +use File::Temp qw(tempdir); +use File::Basename qw(basename); +use File::Spec; +use Cwd; + +use parent qw(Dpkg::Compression::FileHandle); + +sub create { + my ($self, %opts) = @_; + $opts{options} ||= []; + my %spawn_opts; + # Possibly run tar from another directory + if ($opts{chdir}) { + $spawn_opts{chdir} = $opts{chdir}; + *$self->{chdir} = $opts{chdir}; + } + # Redirect input/output appropriately + $self->ensure_open('w'); + $spawn_opts{to_handle} = $self->get_filehandle(); + $spawn_opts{from_pipe} = \*$self->{tar_input}; + # Call tar creation process + $spawn_opts{delete_env} = [ 'TAR_OPTIONS' ]; + $spawn_opts{exec} = [ 'tar', '--null', '-T', '-', '--numeric-owner', + '--owner', '0', '--group', '0', + @{$opts{options}}, '-cf', '-' ]; + *$self->{pid} = spawn(%spawn_opts); + *$self->{cwd} = getcwd(); +} + +sub _add_entry { + my ($self, $file) = @_; + my $cwd = *$self->{cwd}; + croak 'call create() first' unless *$self->{tar_input}; + $file = $2 if ($file =~ /^\Q$cwd\E\/(.+)$/); # Relative names + print({ *$self->{tar_input} } "$file\0") + or syserr(_g('write on tar input')); +} + +sub add_file { + my ($self, $file) = @_; + my $testfile = $file; + if (*$self->{chdir}) { + $testfile = File::Spec->catfile(*$self->{chdir}, $file); + } + croak 'add_file() does not handle directories' + if not -l $testfile and -d _; + $self->_add_entry($file); +} + +sub add_directory { + my ($self, $file) = @_; + my $testfile = $file; + if (*$self->{chdir}) { + $testfile = File::Spec->catdir(*$self->{chdir}, $file); + } + croak 'add_directory() only handles directories' + if -l $testfile or not -d _; + $self->_add_entry($file); +} + +sub finish { + my ($self) = @_; + close(*$self->{tar_input}) or syserr(_g('close on tar input')); + wait_child(*$self->{pid}, cmdline => 'tar -cf -'); + delete *$self->{pid}; + delete *$self->{tar_input}; + delete *$self->{cwd}; + delete *$self->{chdir}; + $self->close(); +} + +sub extract { + my ($self, $dest, %opts) = @_; + $opts{options} ||= []; + $opts{in_place} ||= 0; + $opts{no_fixperms} ||= 0; + my %spawn_opts = (wait_child => 1); + + # Prepare destination + my $tmp; + if ($opts{in_place}) { + $spawn_opts{chdir} = $dest; + $tmp = $dest; # So that fixperms call works + } else { + my $template = basename($self->get_filename()) . '.tmp-extract.XXXXX'; + unless (-e $dest) { + # Kludge so that realpath works + mkdir($dest) or syserr(_g('cannot create directory %s'), $dest); + } + $tmp = tempdir($template, DIR => Cwd::realpath("$dest/.."), CLEANUP => 1); + $spawn_opts{chdir} = $tmp; + } + + # Prepare stuff that handles the input of tar + $self->ensure_open('r'); + $spawn_opts{from_handle} = $self->get_filehandle(); + + # Call tar extraction process + $spawn_opts{delete_env} = [ 'TAR_OPTIONS' ]; + $spawn_opts{exec} = [ 'tar', '--no-same-owner', '--no-same-permissions', + @{$opts{options}}, '-xf', '-' ]; + spawn(%spawn_opts); + $self->close(); + + # Fix permissions on extracted files because tar insists on applying + # our umask _to the original permissions_ rather than mostly-ignoring + # the original permissions. + # We still need --no-same-permissions because otherwise tar might + # extract directory setgid (which we want inherited, not + # extracted); we need --no-same-owner because putting the owner + # back is tedious - in particular, correct group ownership would + # have to be calculated using mount options and other madness. + fixperms($tmp) unless $opts{no_fixperms}; + + # Stop here if we extracted in-place as there's nothing to move around + return if $opts{in_place}; + + # Rename extracted directory + opendir(my $dir_dh, $tmp) or syserr(_g('cannot opendir %s'), $tmp); + my @entries = grep { $_ ne '.' && $_ ne '..' } readdir($dir_dh); + closedir($dir_dh); + my $done = 0; + erasedir($dest); + if (scalar(@entries) == 1 && ! -l "$tmp/$entries[0]" && -d _) { + rename("$tmp/$entries[0]", $dest) + or syserr(_g('unable to rename %s to %s'), + "$tmp/$entries[0]", $dest); + } else { + rename($tmp, $dest) + or syserr(_g('unable to rename %s to %s'), $tmp, $dest); + } + erasedir($tmp); +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Source/Functions.pm b/third_party/dpkg-dev/scripts/Dpkg/Source/Functions.pm new file mode 100644 index 0000000..b61d0af0 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Source/Functions.pm
@@ -0,0 +1,120 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Source::Functions; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use Exporter qw(import); +our @EXPORT_OK = qw(erasedir fixperms fs_time is_binary); + +use Dpkg::ErrorHandling; +use Dpkg::Gettext; +use Dpkg::IPC; + +use POSIX qw(:errno_h); + +sub erasedir { + my ($dir) = @_; + if (not lstat($dir)) { + return if $! == ENOENT; + syserr(_g('cannot stat directory %s (before removal)'), $dir); + } + system 'rm','-rf','--',$dir; + subprocerr("rm -rf $dir") if $?; + if (not stat($dir)) { + return if $! == ENOENT; + syserr(_g("unable to check for removal of dir `%s'"), $dir); + } + error(_g("rm -rf failed to remove `%s'"), $dir); +} + +sub fixperms { + my ($dir) = @_; + my ($mode, $modes_set); + # Unfortunately tar insists on applying our umask _to the original + # permissions_ rather than mostly-ignoring the original + # permissions. We fix it up with chmod -R (which saves us some + # work) but we have to construct a u+/- string which is a bit + # of a palaver. (Numeric doesn't work because we need [ugo]+X + # and [ugo]=<stuff> doesn't work because that unsets sgid on dirs.) + $mode = 0777 & ~umask; + for my $i (0 .. 2) { + $modes_set .= ',' if $i; + $modes_set .= qw(u g o)[$i]; + for my $j (0 .. 2) { + $modes_set .= $mode & (0400 >> ($i * 3 + $j)) ? '+' : '-'; + $modes_set .= qw(r w X)[$j]; + } + } + system('chmod', '-R', '--', $modes_set, $dir); + subprocerr("chmod -R -- $modes_set $dir") if $?; +} + +# Touch the file and read the resulting mtime. +# +# If the file doesn't exist, create it, read the mtime and unlink it. +# +# Use this instead of time() when the timestamp is going to be +# used to set file timestamps. This avoids confusion when an +# NFS server and NFS client disagree about what time it is. +sub fs_time($) { + my ($file) = @_; + my $is_temp = 0; + if (not -e $file) { + open(my $temp_fh, '>', $file) or syserr(_g('cannot write %s')); + close($temp_fh); + $is_temp = 1; + } else { + utime(undef, undef, $file) or + syserr(_g('cannot change timestamp for %s'), $file); + } + stat($file) or syserr(_g('cannot read timestamp from %s'), $file); + my $mtime = (stat(_))[9]; + unlink($file) if $is_temp; + return $mtime; +} + +sub is_binary($) { + my ($file) = @_; + + # TODO: might want to reimplement what diff does, aka checking if the + # file contains \0 in the first 4Kb of data + + # Use diff to check if it's a binary file + my $diffgen; + my $diff_pid = spawn( + exec => [ 'diff', '-u', '--', '/dev/null', $file ], + env => { LC_ALL => 'C', LANG => 'C', TZ => 'UTC0' }, + to_pipe => \$diffgen, + ); + my $result = 0; + local $_; + while (<$diffgen>) { + if (m/^(?:binary|[^-+\@ ].*\bdiffer\b)/i) { + $result = 1; + last; + } elsif (m/^[-+\@ ]/) { + $result = 0; + last; + } + } + close($diffgen) or syserr('close on diff pipe'); + wait_child($diff_pid, nocheck => 1, cmdline => "diff -u -- /dev/null $file"); + return $result; +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Source/Package.pm b/third_party/dpkg-dev/scripts/Dpkg/Source/Package.pm new file mode 100644 index 0000000..ce07a79c --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Source/Package.pm
@@ -0,0 +1,631 @@ +# Copyright © 2008-2011 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Source::Package; + +=encoding utf8 + +=head1 NAME + +Dpkg::Source::Package - manipulate Debian source packages + +=head1 DESCRIPTION + +This module provides an object that can manipulate Debian source +packages. While it supports both the extraction and the creation +of source packages, the only API that is officially supported +is the one that supports the extraction of the source package. + +=head1 FUNCTIONS + +=cut + +use strict; +use warnings; + +our $VERSION = '1.01'; + +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Control; +use Dpkg::Checksums; +use Dpkg::Version; +use Dpkg::Compression; +use Dpkg::Exit qw(run_exit_handlers); +use Dpkg::Path qw(check_files_are_the_same find_command); +use Dpkg::IPC; +use Dpkg::Vendor qw(run_vendor_hook); + +use Carp; +use POSIX qw(:errno_h :sys_wait_h); +use File::Basename; + +use Exporter qw(import); +our @EXPORT_OK = qw(get_default_diff_ignore_regex + set_default_diff_ignore_regex + get_default_tar_ignore_pattern); + +my $diff_ignore_default_regex = ' +# Ignore general backup files +(?:^|/).*~$| +# Ignore emacs recovery files +(?:^|/)\.#.*$| +# Ignore vi swap files +(?:^|/)\..*\.sw.$| +# Ignore baz-style junk files or directories +(?:^|/),,.*(?:$|/.*$)| +# File-names that should be ignored (never directories) +(?:^|/)(?:DEADJOE|\.arch-inventory|\.(?:bzr|cvs|hg|git)ignore)$| +# File or directory names that should be ignored +(?:^|/)(?:CVS|RCS|\.deps|\{arch\}|\.arch-ids|\.svn| +\.hg(?:tags|sigs)?|_darcs|\.git(?:attributes|modules)?| +\.shelf|_MTN|\.be|\.bzr(?:\.backup|tags)?)(?:$|/.*$) +'; +# Take out comments and newlines +$diff_ignore_default_regex =~ s/^#.*$//mg; +$diff_ignore_default_regex =~ s/\n//sg; + +# Public variables +# XXX: Backwards compatibility, stop exporting on VERSION 2.00. +## no critic (Variables::ProhibitPackageVars) +our $diff_ignore_default_regexp; +*diff_ignore_default_regexp = \$diff_ignore_default_regex; + +no warnings 'qw'; ## no critic (TestingAndDebugging::ProhibitNoWarnings) +our @tar_ignore_default_pattern = qw( +*.a +*.la +*.o +*.so +.*.sw? +*/*~ +,,* +.[#~]* +.arch-ids +.arch-inventory +.be +.bzr +.bzr.backup +.bzr.tags +.bzrignore +.cvsignore +.deps +.git +.gitattributes +.gitignore +.gitmodules +.hg +.hgignore +.hgsigs +.hgtags +.shelf +.svn +CVS +DEADJOE +RCS +_MTN +_darcs +{arch} +); +## use critic + +=over 4 + +=item my $string = get_default_diff_ignore_regex() + +Returns the default diff ignore regex. + +=cut + +sub get_default_diff_ignore_regex { + return $diff_ignore_default_regex; +} + +=item set_default_diff_ignore_regex($string) + +Set a regex as the new default diff ignore regex. + +=cut + +sub set_default_diff_ignore_regex { + my ($regex) = @_; + + $diff_ignore_default_regex = $regex; +} + +=item my @array = get_default_tar_ignore_pattern() + +Returns the default tar ignore pattern, as an array. + +=cut + +sub get_default_tar_ignore_pattern { + return @tar_ignore_default_pattern; +} + +=item $p = Dpkg::Source::Package->new(filename => $dscfile, options => {}) + +Creates a new object corresponding to the source package described +by the file $dscfile. + +The options hash supports the following options: + +=over 8 + +=item skip_debianization + +If set to 1, do not apply Debian changes on the extracted source package. + +=item skip_patches + +If set to 1, do not apply Debian-specific patches. This options is +specific for source packages using format "2.0" and "3.0 (quilt)". + +=item require_valid_signature + +If set to 1, the check_signature() method will be stricter and will error +out if the signature can't be verified. + +=item copy_orig_tarballs + +If set to 1, the extraction will copy the upstream tarballs next the +target directory. This is useful if you want to be able to rebuild the +source package after its extraction. + +=back + +=cut + +# Object methods +sub new { + my ($this, %args) = @_; + my $class = ref($this) || $this; + my $self = { + fields => Dpkg::Control->new(type => CTRL_PKG_SRC), + options => {}, + checksums => Dpkg::Checksums->new(), + }; + bless $self, $class; + if (exists $args{options}) { + $self->{options} = $args{options}; + } + if (exists $args{filename}) { + $self->initialize($args{filename}); + $self->init_options(); + } + return $self; +} + +sub init_options { + my ($self) = @_; + # Use full ignore list by default + # note: this function is not called by V1 packages + $self->{options}{diff_ignore_regex} ||= $diff_ignore_default_regex; + $self->{options}{diff_ignore_regex} .= '|(?:^|/)debian/source/local-.*$'; + if (defined $self->{options}{tar_ignore}) { + $self->{options}{tar_ignore} = [ @tar_ignore_default_pattern ] + unless @{$self->{options}{tar_ignore}}; + } else { + $self->{options}{tar_ignore} = [ @tar_ignore_default_pattern ]; + } + push @{$self->{options}{tar_ignore}}, 'debian/source/local-options', + 'debian/source/local-patch-header'; + # Skip debianization while specific to some formats has an impact + # on code common to all formats + $self->{options}{skip_debianization} ||= 0; +} + +sub initialize { + my ($self, $filename) = @_; + my ($fn, $dir) = fileparse($filename); + error(_g('%s is not the name of a file'), $filename) unless $fn; + $self->{basedir} = $dir || './'; + $self->{filename} = $fn; + + # Read the fields + my $fields = Dpkg::Control->new(type => CTRL_PKG_SRC); + $fields->load($filename); + $self->{fields} = $fields; + $self->{is_signed} = $fields->get_option('is_pgp_signed'); + + foreach my $f (qw(Source Version Files)) { + unless (defined($fields->{$f})) { + error(_g('missing critical source control field %s'), $f); + } + } + + $self->{checksums}->add_from_control($fields, use_files_for_md5 => 1); + + $self->upgrade_object_type(0); +} + +sub upgrade_object_type { + my ($self, $update_format) = @_; + $update_format //= 1; + $self->{fields}{'Format'} = '1.0' + unless exists $self->{fields}{'Format'}; + my $format = $self->{fields}{'Format'}; + + if ($format =~ /^([\d\.]+)(?:\s+\((.*)\))?$/) { + my ($version, $variant, $major, $minor) = ($1, $2, $1, undef); + + if (defined $variant and $variant ne lc $variant) { + error(_g("source package format '%s' is not supported: %s"), + $format, _g('format variant must be in lowercase')); + } + + $major =~ s/\.[\d\.]+$//; + my $module = "Dpkg::Source::Package::V$major"; + $module .= '::' . ucfirst $variant if defined $variant; + eval "require $module; \$minor = \$${module}::CURRENT_MINOR_VERSION;"; + $minor //= 0; + if ($update_format) { + $self->{fields}{'Format'} = "$major.$minor"; + $self->{fields}{'Format'} .= " ($variant)" if defined $variant; + } + if ($@) { + error(_g("source package format '%s' is not supported: %s"), + $format, $@); + } + bless $self, $module; + } else { + error(_g("invalid Format field `%s'"), $format); + } +} + +=item $p->get_filename() + +Returns the filename of the DSC file. + +=cut + +sub get_filename { + my ($self) = @_; + return $self->{basedir} . $self->{filename}; +} + +=item $p->get_files() + +Returns the list of files referenced by the source package. The filenames +usually do not have any path information. + +=cut + +sub get_files { + my ($self) = @_; + return $self->{checksums}->get_files(); +} + +=item $p->check_checksums() + +Verify the checksums embedded in the DSC file. It requires the presence of +the other files constituting the source package. If any inconsistency is +discovered, it immediately errors out. + +=cut + +sub check_checksums { + my ($self) = @_; + my $checksums = $self->{checksums}; + # add_from_file verify the checksums if they are already existing + foreach my $file ($checksums->get_files()) { + $checksums->add_from_file($self->{basedir} . $file, key => $file); + } +} + +sub get_basename { + my ($self, $with_revision) = @_; + my $f = $self->{fields}; + unless (exists $f->{'Source'} and exists $f->{'Version'}) { + error(_g('source and version are required to compute the source basename')); + } + my $v = Dpkg::Version->new($f->{'Version'}); + my $vs = $v->as_string(omit_epoch => 1, omit_revision => !$with_revision); + return $f->{'Source'} . '_' . $vs; +} + +sub find_original_tarballs { + my ($self, %opts) = @_; + $opts{extension} = compression_get_file_extension_regex() + unless exists $opts{extension}; + $opts{include_main} = 1 unless exists $opts{include_main}; + $opts{include_supplementary} = 1 unless exists $opts{include_supplementary}; + my $basename = $self->get_basename(); + my @tar; + foreach my $dir ('.', $self->{basedir}, $self->{options}{origtardir}) { + next unless defined($dir) and -d $dir; + opendir(my $dir_dh, $dir) or syserr(_g('cannot opendir %s'), $dir); + push @tar, map { "$dir/$_" } grep { + ($opts{include_main} and + /^\Q$basename\E\.orig\.tar\.$opts{extension}$/) or + ($opts{include_supplementary} and + /^\Q$basename\E\.orig-[[:alnum:]-]+\.tar\.$opts{extension}$/) + } readdir($dir_dh); + closedir($dir_dh); + } + return @tar; +} + +=item $bool = $p->is_signed() + +Returns 1 if the DSC files contains an embedded OpenPGP signature. +Otherwise returns 0. + +=cut + +sub is_signed { + my $self = shift; + return $self->{is_signed}; +} + +=item $p->check_signature() + +Implement the same OpenPGP signature check that dpkg-source does. +In case of problems, it prints a warning or errors out. + +If the object has been created with the "require_valid_signature" option, +then any problem will result in a fatal error. + +=cut + +sub check_signature { + my ($self) = @_; + my $dsc = $self->get_filename(); + my @exec; + + if (find_command('gpgv2')) { + push @exec, 'gpgv2'; + } elsif (find_command('gpgv')) { + push @exec, 'gpgv'; + } elsif (find_command('gpg2')) { + push @exec, 'gpg2', '--no-default-keyring', '-q', '--verify'; + } elsif (find_command('gpg')) { + push @exec, 'gpg', '--no-default-keyring', '-q', '--verify'; + } + if (scalar(@exec)) { + if (defined $ENV{HOME} and -r "$ENV{HOME}/.gnupg/trustedkeys.gpg") { + push @exec, '--keyring', "$ENV{HOME}/.gnupg/trustedkeys.gpg"; + } + foreach my $vendor_keyring (run_vendor_hook('keyrings')) { + if (-r $vendor_keyring) { + push @exec, '--keyring', $vendor_keyring; + } + } + push @exec, $dsc; + + my ($stdout, $stderr); + spawn(exec => \@exec, wait_child => 1, nocheck => 1, + to_string => \$stdout, error_to_string => \$stderr, + timeout => 10); + if (WIFEXITED($?)) { + my $gpg_status = WEXITSTATUS($?); + print { *STDERR } "$stdout$stderr" if $gpg_status; + if ($gpg_status == 1 or ($gpg_status && + $self->{options}{require_valid_signature})) + { + error(_g('failed to verify signature on %s'), $dsc); + } elsif ($gpg_status) { + warning(_g('failed to verify signature on %s'), $dsc); + } + } else { + subprocerr("@exec"); + } + } else { + if ($self->{options}{require_valid_signature}) { + error(_g("could not verify signature on %s since gpg isn't installed"), $dsc); + } else { + warning(_g("could not verify signature on %s since gpg isn't installed"), $dsc); + } + } +} + +sub parse_cmdline_options { + my ($self, @opts) = @_; + foreach (@opts) { + if (not $self->parse_cmdline_option($_)) { + warning(_g('%s is not a valid option for %s'), $_, ref($self)); + } + } +} + +sub parse_cmdline_option { + return 0; +} + +=item $p->extract($targetdir) + +Extracts the source package in the target directory $targetdir. Beware +that if $targetdir already exists, it will be erased. + +=cut + +sub extract { + my $self = shift; + my $newdirectory = $_[0]; + + my ($ok, $error) = version_check($self->{fields}{'Version'}); + error($error) unless $ok; + + # Copy orig tarballs + if ($self->{options}{copy_orig_tarballs}) { + my $basename = $self->get_basename(); + my ($dirname, $destdir) = fileparse($newdirectory); + $destdir ||= './'; + my $ext = compression_get_file_extension_regex(); + foreach my $orig (grep { /^\Q$basename\E\.orig(-[[:alnum:]-]+)?\.tar\.$ext$/ } + $self->get_files()) + { + my $src = File::Spec->catfile($self->{basedir}, $orig); + my $dst = File::Spec->catfile($destdir, $orig); + if (not check_files_are_the_same($src, $dst, 1)) { + system('cp', '--', $src, $dst); + subprocerr("cp $src to $dst") if $?; + } + } + } + + # Try extract + eval { $self->do_extract(@_) }; + if ($@) { + run_exit_handlers(); + die $@; + } + + # Store format if non-standard so that next build keeps the same format + if ($self->{fields}{'Format'} ne '1.0' and + not $self->{options}{skip_debianization}) + { + my $srcdir = File::Spec->catdir($newdirectory, 'debian', 'source'); + my $format_file = File::Spec->catfile($srcdir, 'format'); + unless (-e $format_file) { + mkdir($srcdir) unless -e $srcdir; + open(my $format_fh, '>', $format_file) + or syserr(_g('cannot write %s'), $format_file); + print { $format_fh } $self->{fields}{'Format'} . "\n"; + close($format_fh); + } + } + + # Make sure debian/rules is executable + my $rules = File::Spec->catfile($newdirectory, 'debian', 'rules'); + my @s = lstat($rules); + if (not scalar(@s)) { + unless ($! == ENOENT) { + syserr(_g('cannot stat %s'), $rules); + } + warning(_g('%s does not exist'), $rules) + unless $self->{options}{skip_debianization}; + } elsif (-f _) { + chmod($s[2] | 0111, $rules) + or syserr(_g('cannot make %s executable'), $rules); + } else { + warning(_g('%s is not a plain file'), $rules); + } +} + +sub do_extract { + croak 'Dpkg::Source::Package does not know how to unpack a ' . + 'source package; use one of the subclasses'; +} + +# Function used specifically during creation of a source package + +sub before_build { + my ($self, $dir) = @_; +} + +sub build { + my $self = shift; + eval { $self->do_build(@_) }; + if ($@) { + run_exit_handlers(); + die $@; + } +} + +sub after_build { + my ($self, $dir) = @_; +} + +sub do_build { + croak 'Dpkg::Source::Package does not know how to build a ' . + 'source package; use one of the subclasses'; +} + +sub can_build { + my ($self, $dir) = @_; + return (0, 'can_build() has not been overriden'); +} + +sub add_file { + my ($self, $filename) = @_; + my ($fn, $dir) = fileparse($filename); + if ($self->{checksums}->has_file($fn)) { + croak "tried to add file '$fn' twice"; + } + $self->{checksums}->add_from_file($filename, key => $fn); + $self->{checksums}->export_to_control($self->{fields}, + use_files_for_md5 => 1); +} + +sub commit { + my $self = shift; + eval { $self->do_commit(@_) }; + if ($@) { + run_exit_handlers(); + die $@; + } +} + +sub do_commit { + my ($self, $dir) = @_; + info(_g("'%s' is not supported by the source format '%s'"), + 'dpkg-source --commit', $self->{fields}{'Format'}); +} + +sub write_dsc { + my ($self, %opts) = @_; + my $fields = $self->{fields}; + + foreach my $f (keys %{$opts{override}}) { + $fields->{$f} = $opts{override}{$f}; + } + + unless($opts{nocheck}) { + foreach my $f (qw(Source Version)) { + unless (defined($fields->{$f})) { + error(_g('missing information for critical output field %s'), $f); + } + } + foreach my $f (qw(Maintainer Architecture Standards-Version)) { + unless (defined($fields->{$f})) { + warning(_g('missing information for output field %s'), $f); + } + } + } + + foreach my $f (keys %{$opts{remove}}) { + delete $fields->{$f}; + } + + my $filename = $opts{filename}; + unless (defined $filename) { + $filename = $self->get_basename(1) . '.dsc'; + } + open(my $dsc_fh, '>', $filename) + or syserr(_g('cannot write %s'), $filename); + $fields->apply_substvars($opts{substvars}); + $fields->output($dsc_fh); + close($dsc_fh); +} + +=back + +=head1 CHANGES + +=head2 Version 1.01 + +New functions: get_default_diff_ignore_regex(), set_default_diff_ignore_regex(), +get_default_tar_ignore_pattern() + +Deprecated variables: $diff_ignore_default_regexp, @tar_ignore_default_pattern + +=head1 AUTHOR + +Raphaël Hertzog, E<lt>hertzog@debian.orgE<gt> + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V1.pm b/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V1.pm new file mode 100644 index 0000000..d0dfa97 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V1.pm
@@ -0,0 +1,402 @@ +# Copyright © 2008-2009 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Source::Package::V1; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use parent qw(Dpkg::Source::Package); + +use Dpkg (); +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Compression; +use Dpkg::Source::Archive; +use Dpkg::Source::Patch; +use Dpkg::Exit qw(push_exit_handler pop_exit_handler); +use Dpkg::Source::Functions qw(erasedir); +use Dpkg::Source::Package::V3::Native; + +use POSIX qw(:errno_h); +use Cwd; +use File::Basename; +use File::Temp qw(tempfile); +use File::Spec; + +our $CURRENT_MINOR_VERSION = '0'; + +sub init_options { + my ($self) = @_; + # Don't call $self->SUPER::init_options() on purpose, V1.0 has no + # ignore by default + if ($self->{options}{diff_ignore_regex}) { + $self->{options}{diff_ignore_regex} .= '|(?:^|/)debian/source/local-.*$'; + } else { + $self->{options}{diff_ignore_regex} = '(?:^|/)debian/source/local-.*$'; + } + push @{$self->{options}{tar_ignore}}, 'debian/source/local-options', + 'debian/source/local-patch-header'; + $self->{options}{sourcestyle} ||= 'X'; + $self->{options}{skip_debianization} ||= 0; + $self->{options}{abort_on_upstream_changes} ||= 0; +} + +sub parse_cmdline_option { + my ($self, $opt) = @_; + my $o = $self->{options}; + if ($opt =~ m/^-s([akpursnAKPUR])$/) { + warning(_g('-s%s option overrides earlier -s%s option'), $1, + $o->{sourcestyle}) if $o->{sourcestyle} ne 'X'; + $o->{sourcestyle} = $1; + $o->{copy_orig_tarballs} = 0 if $1 eq 'n'; # Extract option -sn + return 1; + } elsif ($opt =~ m/^--skip-debianization$/) { + $o->{skip_debianization} = 1; + return 1; + } elsif ($opt =~ m/^--abort-on-upstream-changes$/) { + $o->{abort_on_upstream_changes} = 1; + return 1; + } + return 0; +} + +sub do_extract { + my ($self, $newdirectory) = @_; + my $sourcestyle = $self->{options}{sourcestyle}; + my $fields = $self->{fields}; + + $sourcestyle =~ y/X/p/; + unless ($sourcestyle =~ m/[pun]/) { + usageerr(_g('source handling style -s%s not allowed with -x'), + $sourcestyle); + } + + my $dscdir = $self->{basedir}; + + my $basename = $self->get_basename(); + my $basenamerev = $self->get_basename(1); + + # V1.0 only supports gzip compression + my ($tarfile, $difffile); + my $tarsign; + foreach my $file ($self->get_files()) { + if ($file =~ /^(?:\Q$basename\E\.orig|\Q$basenamerev\E)\.tar\.gz$/) { + error(_g('multiple tarfiles in v1.0 source package')) if $tarfile; + $tarfile = $file; + } elsif ($file =~ /^\Q$basename\E\.orig\.tar\.gz\.asc$/) { + $tarsign = $file; + } elsif ($file =~ /^\Q$basenamerev\E\.diff\.gz$/) { + $difffile = $file; + } else { + error(_g('unrecognized file for a %s source package: %s'), + 'v1.0', $file); + } + } + + error(_g('no tarfile in Files field')) unless $tarfile; + my $native = $difffile ? 0 : 1; + if ($native and ($tarfile =~ /\.orig\.tar\.gz$/)) { + warning(_g('native package with .orig.tar')); + $native = 0; # V3::Native doesn't handle orig.tar + } + + if ($native) { + Dpkg::Source::Package::V3::Native::do_extract($self, $newdirectory); + } else { + my $expectprefix = $newdirectory; + $expectprefix .= '.orig'; + + erasedir($newdirectory); + if (-e $expectprefix) { + rename($expectprefix, "$newdirectory.tmp-keep") + or syserr(_g("unable to rename `%s' to `%s'"), $expectprefix, + "$newdirectory.tmp-keep"); + } + + info(_g('unpacking %s'), $tarfile); + my $tar = Dpkg::Source::Archive->new(filename => "$dscdir$tarfile"); + $tar->extract($expectprefix); + + if ($sourcestyle =~ /u/) { + # -su: keep .orig directory unpacked + if (-e "$newdirectory.tmp-keep") { + error(_g('unable to keep orig directory (already exists)')); + } + system('cp', '-ar', '--', $expectprefix, "$newdirectory.tmp-keep"); + subprocerr("cp $expectprefix to $newdirectory.tmp-keep") if $?; + } + + rename($expectprefix, $newdirectory) + or syserr(_g('failed to rename newly-extracted %s to %s'), + $expectprefix, $newdirectory); + + # rename the copied .orig directory + if (-e "$newdirectory.tmp-keep") { + rename("$newdirectory.tmp-keep", $expectprefix) + or syserr(_g('failed to rename saved %s to %s'), + "$newdirectory.tmp-keep", $expectprefix); + } + } + + if ($difffile and not $self->{options}{skip_debianization}) { + my $patch = "$dscdir$difffile"; + info(_g('applying %s'), $difffile); + my $patch_obj = Dpkg::Source::Patch->new(filename => $patch); + my $analysis = $patch_obj->apply($newdirectory, force_timestamp => 1); + my @files = grep { ! m{^\Q$newdirectory\E/debian/} } + sort keys %{$analysis->{filepatched}}; + info(_g('upstream files that have been modified: %s'), + "\n " . join("\n ", @files)) if scalar @files; + } +} + +sub can_build { + my ($self, $dir) = @_; + + # As long as we can use gzip, we can do it as we have + # native packages as fallback + return (0, _g('only supports gzip compression')) + unless $self->{options}{compression} eq 'gzip'; + return 1; +} + +sub do_build { + my ($self, $dir) = @_; + my $sourcestyle = $self->{options}{sourcestyle}; + my @argv = @{$self->{options}{ARGV}}; + my @tar_ignore = map { "--exclude=$_" } @{$self->{options}{tar_ignore}}; + my $diff_ignore_regex = $self->{options}{diff_ignore_regex}; + + if (scalar(@argv) > 1) { + usageerr(_g('-b takes at most a directory and an orig source ' . + 'argument (with v1.0 source package)')); + } + + $sourcestyle =~ y/X/A/; + unless ($sourcestyle =~ m/[akpursnAKPUR]/) { + usageerr(_g('source handling style -s%s not allowed with -b'), + $sourcestyle); + } + + my $sourcepackage = $self->{fields}{'Source'}; + my $basenamerev = $self->get_basename(1); + my $basename = $self->get_basename(); + my $basedirname = $basename; + $basedirname =~ s/_/-/; + + # Try to find a .orig tarball for the package + my $origdir = "$dir.orig"; + my $origtargz = $self->get_basename() . '.orig.tar.gz'; + if (-e $origtargz) { + unless (-f $origtargz) { + error(_g("packed orig `%s' exists but is not a plain file"), $origtargz); + } + } else { + $origtargz = undef; + } + + if (@argv) { + # We have a second-argument <orig-dir> or <orig-targz>, check what it + # is to decide the mode to use + my $origarg = shift(@argv); + if (length($origarg)) { + stat($origarg) + or syserr(_g('cannot stat orig argument %s'), $origarg); + if (-d _) { + $origdir = File::Spec->catdir($origarg); + + $sourcestyle =~ y/aA/rR/; + unless ($sourcestyle =~ m/[ursURS]/) { + error(_g('orig argument is unpacked but source handling ' . + 'style -s%s calls for packed (.orig.tar.<ext>)'), + $sourcestyle); + } + } elsif (-f _) { + $origtargz = $origarg; + $sourcestyle =~ y/aA/pP/; + unless ($sourcestyle =~ m/[kpsKPS]/) { + error(_g('orig argument is packed but source handling ' . + 'style -s%s calls for unpacked (.orig/)'), + $sourcestyle); + } + } else { + error(_g('orig argument %s is not a plain file or directory'), + $origarg); + } + } else { + $sourcestyle =~ y/aA/nn/; + unless ($sourcestyle =~ m/n/) { + error(_g('orig argument is empty (means no orig, no diff) ' . + 'but source handling style -s%s wants something'), + $sourcestyle); + } + } + } elsif ($sourcestyle =~ m/[aA]/) { + # We have no explicit <orig-dir> or <orig-targz>, try to use + # a .orig tarball first, then a .orig directory and fall back to + # creating a native .tar.gz + if ($origtargz) { + $sourcestyle =~ y/aA/pP/; # .orig.tar.<ext> + } else { + if (stat($origdir)) { + unless (-d _) { + error(_g("unpacked orig `%s' exists but is not a directory"), + $origdir); + } + $sourcestyle =~ y/aA/rR/; # .orig directory + } elsif ($! != ENOENT) { + syserr(_g("unable to stat putative unpacked orig `%s'"), $origdir); + } else { + $sourcestyle =~ y/aA/nn/; # Native tar.gz + } + } + } + + my ($dirname, $dirbase) = fileparse($dir); + if ($dirname ne $basedirname) { + warning(_g("source directory '%s' is not <sourcepackage>" . + "-<upstreamversion> '%s'"), $dir, $basedirname); + } + + my ($tarname, $tardirname, $tardirbase); + if ($sourcestyle ne 'n') { + my ($origdirname, $origdirbase) = fileparse($origdir); + + if ($origdirname ne "$basedirname.orig") { + warning(_g('.orig directory name %s is not <package>' . + '-<upstreamversion> (wanted %s)'), + $origdirname, "$basedirname.orig"); + } + $tardirbase = $origdirbase; + $tardirname = $origdirname; + + $tarname = $origtargz || "$basename.orig.tar.gz"; + unless ($tarname =~ /\Q$basename\E\.orig\.tar\.gz/) { + warning(_g('.orig.tar name %s is not <package>_<upstreamversion>' . + '.orig.tar (wanted %s)'), + $tarname, "$basename.orig.tar.gz"); + } + } + + if ($sourcestyle eq 'n') { + $self->{options}{ARGV} = []; # ensure we have no error + Dpkg::Source::Package::V3::Native::do_build($self, $dir); + } elsif ($sourcestyle =~ m/[nurUR]/) { + if (stat($tarname)) { + unless ($sourcestyle =~ m/[nUR]/) { + error(_g("tarfile `%s' already exists, not overwriting, " . + 'giving up; use -sU or -sR to override'), $tarname); + } + } elsif ($! != ENOENT) { + syserr(_g("unable to check for existence of `%s'"), $tarname); + } + + info(_g('building %s in %s'), + $sourcepackage, $tarname); + + my ($ntfh, $newtar) = tempfile("$tarname.new.XXXXXX", + DIR => getcwd(), UNLINK => 0); + my $tar = Dpkg::Source::Archive->new(filename => $newtar, + compression => compression_guess_from_filename($tarname), + compression_level => $self->{options}{comp_level}); + $tar->create(options => \@tar_ignore, chdir => $tardirbase); + $tar->add_directory($tardirname); + $tar->finish(); + rename($newtar, $tarname) + or syserr(_g("unable to rename `%s' (newly created) to `%s'"), + $newtar, $tarname); + chmod(0666 &~ umask(), $tarname) + or syserr(_g("unable to change permission of `%s'"), $tarname); + } else { + info(_g('building %s using existing %s'), + $sourcepackage, $tarname); + } + + $self->add_file($tarname) if $tarname; + + if ($sourcestyle =~ m/[kpKP]/) { + if (stat($origdir)) { + unless ($sourcestyle =~ m/[KP]/) { + error(_g("orig dir `%s' already exists, not overwriting, ". + 'giving up; use -sA, -sK or -sP to override'), + $origdir); + } + push_exit_handler(sub { erasedir($origdir) }); + erasedir($origdir); + pop_exit_handler(); + } elsif ($! != ENOENT) { + syserr(_g("unable to check for existence of orig dir `%s'"), + $origdir); + } + + my $tar = Dpkg::Source::Archive->new(filename => $origtargz); + $tar->extract($origdir); + } + + my $ur; # Unrepresentable changes + if ($sourcestyle =~ m/[kpursKPUR]/) { + my $diffname = "$basenamerev.diff.gz"; + info(_g('building %s in %s'), + $sourcepackage, $diffname); + my ($ndfh, $newdiffgz) = tempfile("$diffname.new.XXXXXX", + DIR => getcwd(), UNLINK => 0); + push_exit_handler(sub { unlink($newdiffgz) }); + my $diff = Dpkg::Source::Patch->new(filename => $newdiffgz, + compression => 'gzip'); + $diff->create(); + $diff->add_diff_directory($origdir, $dir, + basedirname => $basedirname, + diff_ignore_regex => $diff_ignore_regex, + options => []); # Force empty set of options to drop the + # default -p option + $diff->finish() || $ur++; + pop_exit_handler(); + + my $analysis = $diff->analyze($origdir); + my @files = grep { ! m{^debian/} } map { s{^[^/]+/+}{}; $_ } + sort keys %{$analysis->{filepatched}}; + if (scalar @files) { + warning(_g('the diff modifies the following upstream files: %s'), + "\n " . join("\n ", @files)); + info(_g("use the '3.0 (quilt)' format to have separate and " . + 'documented changes to upstream files, see dpkg-source(1)')); + error(_g('aborting due to --abort-on-upstream-changes')) + if $self->{options}{abort_on_upstream_changes}; + } + + rename($newdiffgz, $diffname) + or syserr(_g("unable to rename `%s' (newly created) to `%s'"), + $newdiffgz, $diffname); + chmod(0666 &~ umask(), $diffname) + or syserr(_g("unable to change permission of `%s'"), $diffname); + + $self->add_file($diffname); + } + + if ($sourcestyle =~ m/[prPR]/) { + erasedir($origdir); + } + + if ($ur) { + printf { *STDERR } _g('%s: unrepresentable changes to source') . "\n", + $Dpkg::PROGNAME; + exit(1); + } +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V2.pm b/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V2.pm new file mode 100644 index 0000000..c5babfb --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V2.pm
@@ -0,0 +1,767 @@ +# Copyright © 2008-2011 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Source::Package::V2; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use parent qw(Dpkg::Source::Package); + +use Dpkg; +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::File; +use Dpkg::Compression; +use Dpkg::Source::Archive; +use Dpkg::Source::Patch; +use Dpkg::Exit qw(push_exit_handler pop_exit_handler); +use Dpkg::Source::Functions qw(erasedir is_binary fs_time); +use Dpkg::Vendor qw(run_vendor_hook); +use Dpkg::Control; +use Dpkg::Changelog::Parse; + +use POSIX qw(:errno_h); +use Cwd; +use File::Basename; +use File::Temp qw(tempfile tempdir); +use File::Path; +use File::Spec; +use File::Find; +use File::Copy; + +our $CURRENT_MINOR_VERSION = '0'; + +sub init_options { + my ($self) = @_; + $self->SUPER::init_options(); + $self->{options}{include_removal} = 0 + unless exists $self->{options}{include_removal}; + $self->{options}{include_timestamp} = 0 + unless exists $self->{options}{include_timestamp}; + $self->{options}{include_binaries} = 0 + unless exists $self->{options}{include_binaries}; + $self->{options}{preparation} = 1 + unless exists $self->{options}{preparation}; + $self->{options}{skip_patches} = 0 + unless exists $self->{options}{skip_patches}; + $self->{options}{unapply_patches} = 'auto' + unless exists $self->{options}{unapply_patches}; + $self->{options}{skip_debianization} = 0 + unless exists $self->{options}{skip_debianization}; + $self->{options}{create_empty_orig} = 0 + unless exists $self->{options}{create_empty_orig}; + $self->{options}{auto_commit} = 0 + unless exists $self->{options}{auto_commit}; +} + +sub parse_cmdline_option { + my ($self, $opt) = @_; + if ($opt =~ /^--include-removal$/) { + $self->{options}{include_removal} = 1; + return 1; + } elsif ($opt =~ /^--include-timestamp$/) { + $self->{options}{include_timestamp} = 1; + return 1; + } elsif ($opt =~ /^--include-binaries$/) { + $self->{options}{include_binaries} = 1; + return 1; + } elsif ($opt =~ /^--no-preparation$/) { + $self->{options}{preparation} = 0; + return 1; + } elsif ($opt =~ /^--skip-patches$/) { + $self->{options}{skip_patches} = 1; + return 1; + } elsif ($opt =~ /^--unapply-patches$/) { + $self->{options}{unapply_patches} = 'yes'; + return 1; + } elsif ($opt =~ /^--no-unapply-patches$/) { + $self->{options}{unapply_patches} = 'no'; + return 1; + } elsif ($opt =~ /^--skip-debianization$/) { + $self->{options}{skip_debianization} = 1; + return 1; + } elsif ($opt =~ /^--create-empty-orig$/) { + $self->{options}{create_empty_orig} = 1; + return 1; + } elsif ($opt =~ /^--abort-on-upstream-changes$/) { + $self->{options}{auto_commit} = 0; + return 1; + } elsif ($opt =~ /^--auto-commit$/) { + $self->{options}{auto_commit} = 1; + return 1; + } + return 0; +} + +sub do_extract { + my ($self, $newdirectory) = @_; + my $fields = $self->{fields}; + + my $dscdir = $self->{basedir}; + + my $basename = $self->get_basename(); + my $basenamerev = $self->get_basename(1); + + my ($tarfile, $debianfile, %origtar, %seen); + my ($tarsign, %origtarsign); + my $re_ext = compression_get_file_extension_regex(); + foreach my $file ($self->get_files()) { + my $uncompressed = $file; + $uncompressed =~ s/\.$re_ext$/.*/; + $uncompressed =~ s/\.$re_ext\.asc$/.*.asc/; + error(_g('duplicate files in %s source package: %s'), 'v2.0', + $uncompressed) if $seen{$uncompressed}; + $seen{$uncompressed} = 1; + if ($file =~ /^\Q$basename\E\.orig\.tar\.$re_ext$/) { + $tarfile = $file; + } elsif ($file =~ /^\Q$basename\E\.orig\.tar\.$re_ext\.asc$/) { + $tarsign = $file; + } elsif ($file =~ /^\Q$basename\E\.orig-([[:alnum:]-]+)\.tar\.$re_ext$/) { + $origtar{$1} = $file; + } elsif ($file =~ /^\Q$basename\E\.orig-([[:alnum:]-]+)\.tar\.$re_ext\.asc$/) { + $origtarsign{$1} = $file; + } elsif ($file =~ /^\Q$basenamerev\E\.debian\.tar\.$re_ext$/) { + $debianfile = $file; + } else { + error(_g('unrecognized file for a %s source package: %s'), + 'v2.0', $file); + } + } + + unless ($tarfile and $debianfile) { + error(_g('missing orig.tar or debian.tar file in v2.0 source package')); + } + if ($tarsign and $tarfile ne substr $tarsign, 0, -4) { + error(_g('mismatched orig.tar %s for signature %s in source package'), + $tarfile, $tarsign); + } + foreach my $name (keys %origtarsign) { + error(_g('missing addon orig.tar for signature %s in source package'), + $origtarsign{$name}) + if not exists $origtar{$name}; + error(_g('mismatched addon orig.tar %s for signature %s in source package'), + $origtar{$name}, $origtarsign{$name}) + if $origtar{$name} ne substr $origtarsign{$name}, 0, -4; + } + + erasedir($newdirectory); + + # Extract main tarball + info(_g('unpacking %s'), $tarfile); + my $tar = Dpkg::Source::Archive->new(filename => "$dscdir$tarfile"); + $tar->extract($newdirectory, no_fixperms => 1, + options => [ '--anchored', '--no-wildcards-match-slash', + '--exclude', '*/.pc', '--exclude', '.pc' ]); + # The .pc exclusion is only needed for 3.0 (quilt) and to avoid + # having an upstream tarball provide a directory with symlinks + # that would be blindly followed when applying the patches + + # Extract additional orig tarballs + foreach my $subdir (keys %origtar) { + my $file = $origtar{$subdir}; + info(_g('unpacking %s'), $file); + if (-e "$newdirectory/$subdir") { + warning(_g("required removal of `%s' installed by original tarball"), $subdir); + erasedir("$newdirectory/$subdir"); + } + $tar = Dpkg::Source::Archive->new(filename => "$dscdir$file"); + $tar->extract("$newdirectory/$subdir", no_fixperms => 1); + } + + # Stop here if debianization is not wanted + return if $self->{options}{skip_debianization}; + + # Extract debian tarball after removing the debian directory + info(_g('unpacking %s'), $debianfile); + erasedir("$newdirectory/debian"); + # Exclude existing symlinks from extraction of debian.tar.gz as we + # don't want to overwrite something outside of $newdirectory due to a + # symlink + my @exclude_symlinks; + my $wanted = sub { + return if not -l $_; + my $fn = File::Spec->abs2rel($_, $newdirectory); + push @exclude_symlinks, '--exclude', $fn; + }; + find({ wanted => $wanted, no_chdir => 1 }, $newdirectory); + $tar = Dpkg::Source::Archive->new(filename => "$dscdir$debianfile"); + $tar->extract($newdirectory, in_place => 1, + options => [ '--anchored', '--no-wildcards', + @exclude_symlinks ]); + + # Apply patches (in a separate method as it might be overriden) + $self->apply_patches($newdirectory, usage => 'unpack') + unless $self->{options}{skip_patches}; +} + +sub get_autopatch_name { + return 'zz_debian-diff-auto'; +} + +sub get_patches { + my ($self, $dir, %opts) = @_; + $opts{skip_auto} //= 0; + my @patches; + my $pd = "$dir/debian/patches"; + my $auto_patch = $self->get_autopatch_name(); + if (-d $pd) { + opendir(my $dir_dh, $pd) or syserr(_g('cannot opendir %s'), $pd); + foreach my $patch (sort readdir($dir_dh)) { + # patches match same rules as run-parts + next unless $patch =~ /^[\w-]+$/ and -f "$pd/$patch"; + next if $opts{skip_auto} and $patch eq $auto_patch; + push @patches, $patch; + } + closedir($dir_dh); + } + return @patches; +} + +sub apply_patches { + my ($self, $dir, %opts) = @_; + $opts{skip_auto} //= 0; + my @patches = $self->get_patches($dir, %opts); + return unless scalar(@patches); + my $applied = File::Spec->catfile($dir, 'debian', 'patches', '.dpkg-source-applied'); + open(my $applied_fh, '>', $applied) + or syserr(_g('cannot write %s'), $applied); + print { $applied_fh } "# During $opts{usage}\n"; + my $timestamp = fs_time($applied); + foreach my $patch ($self->get_patches($dir, %opts)) { + my $path = File::Spec->catfile($dir, 'debian', 'patches', $patch); + info(_g('applying %s'), $patch) unless $opts{skip_auto}; + my $patch_obj = Dpkg::Source::Patch->new(filename => $path); + $patch_obj->apply($dir, force_timestamp => 1, + timestamp => $timestamp, + add_options => [ '-E' ]); + print { $applied_fh } "$patch\n"; + } + close($applied_fh); +} + +sub unapply_patches { + my ($self, $dir, %opts) = @_; + my @patches = reverse($self->get_patches($dir, %opts)); + return unless scalar(@patches); + my $applied = File::Spec->catfile($dir, 'debian', 'patches', '.dpkg-source-applied'); + my $timestamp = fs_time($applied); + foreach my $patch (@patches) { + my $path = File::Spec->catfile($dir, 'debian', 'patches', $patch); + info(_g('unapplying %s'), $patch) unless $opts{quiet}; + my $patch_obj = Dpkg::Source::Patch->new(filename => $path); + $patch_obj->apply($dir, force_timestamp => 1, verbose => 0, + timestamp => $timestamp, + add_options => [ '-E', '-R' ]); + } + unlink($applied); +} + +sub upstream_tarball_template { + my ($self) = @_; + my $ext = '{' . join(',', + sort map { + compression_get_property($_, 'file_ext') + } compression_get_list()) . '}'; + return '../' . $self->get_basename() . ".orig.tar.$ext"; +} + +sub can_build { + my ($self, $dir) = @_; + return 1 if $self->find_original_tarballs(include_supplementary => 0); + return 1 if $self->{options}{create_empty_orig} and + $self->find_original_tarballs(include_main => 0); + return (0, sprintf(_g('no upstream tarball found at %s'), + $self->upstream_tarball_template())); +} + +sub before_build { + my ($self, $dir) = @_; + $self->check_patches_applied($dir) if $self->{options}{preparation}; +} + +sub after_build { + my ($self, $dir) = @_; + my $applied = File::Spec->catfile($dir, 'debian', 'patches', '.dpkg-source-applied'); + my $reason = ''; + if (-e $applied) { + open(my $applied_fh, '<', $applied) + or syserr(_g('cannot read %s'), $applied); + $reason = <$applied_fh>; + close($applied_fh); + } + my $opt_unapply = $self->{options}{unapply_patches}; + if (($opt_unapply eq 'auto' and $reason =~ /^# During preparation/) or + $opt_unapply eq 'yes') { + $self->unapply_patches($dir); + } +} + +sub prepare_build { + my ($self, $dir) = @_; + $self->{diff_options} = { + diff_ignore_regex => $self->{options}{diff_ignore_regex} . + '|(^|/)debian/patches/.dpkg-source-applied$', + include_removal => $self->{options}{include_removal}, + include_timestamp => $self->{options}{include_timestamp}, + use_dev_null => 1, + }; + push @{$self->{options}{tar_ignore}}, 'debian/patches/.dpkg-source-applied'; + $self->check_patches_applied($dir) if $self->{options}{preparation}; + if ($self->{options}{create_empty_orig} and + not $self->find_original_tarballs(include_supplementary => 0)) + { + # No main orig.tar, create a dummy one + my $filename = $self->get_basename() . '.orig.tar.' . + $self->{options}{comp_ext}; + my $tar = Dpkg::Source::Archive->new(filename => $filename); + $tar->create(); + $tar->finish(); + } +} + +sub check_patches_applied { + my ($self, $dir) = @_; + my $applied = File::Spec->catfile($dir, 'debian', 'patches', '.dpkg-source-applied'); + unless (-e $applied) { + info(_g('patches are not applied, applying them now')); + $self->apply_patches($dir, usage => 'preparation'); + } +} + +sub generate_patch { + my ($self, $dir, %opts) = @_; + my ($dirname, $updir) = fileparse($dir); + my $basedirname = $self->get_basename(); + $basedirname =~ s/_/-/; + + # Identify original tarballs + my ($tarfile, %origtar); + my $comp_ext_regex = compression_get_file_extension_regex(); + my @origtarballs; + foreach (sort $self->find_original_tarballs()) { + if (/\.orig\.tar\.$comp_ext_regex$/) { + if (defined($tarfile)) { + error(_g('several orig.tar files found (%s and %s) but only ' . + 'one is allowed'), $tarfile, $_); + } + $tarfile = $_; + push @origtarballs, $_; + $self->add_file($_); + } elsif (/\.orig-([[:alnum:]-]+)\.tar\.$comp_ext_regex$/) { + $origtar{$1} = $_; + push @origtarballs, $_; + $self->add_file($_); + } + } + + error(_g('no upstream tarball found at %s'), + $self->upstream_tarball_template()) unless $tarfile; + + if ($opts{usage} eq 'build') { + info(_g('building %s using existing %s'), + $self->{fields}{'Source'}, "@origtarballs"); + } + + # Unpack a second copy for comparison + my $tmp = tempdir("$dirname.orig.XXXXXX", DIR => $updir); + push_exit_handler(sub { erasedir($tmp) }); + + # Extract main tarball + my $tar = Dpkg::Source::Archive->new(filename => $tarfile); + $tar->extract($tmp); + + # Extract additional orig tarballs + foreach my $subdir (keys %origtar) { + my $file = $origtar{$subdir}; + $tar = Dpkg::Source::Archive->new(filename => $file); + $tar->extract("$tmp/$subdir"); + } + + # Copy over the debian directory + erasedir("$tmp/debian"); + system('cp', '-a', '--', "$dir/debian", "$tmp/"); + subprocerr(_g('copy of the debian directory')) if $?; + + # Apply all patches except the last automatic one + $opts{skip_auto} //= 0; + $self->apply_patches($tmp, skip_auto => $opts{skip_auto}, usage => 'build'); + + # Create a patch + my ($difffh, $tmpdiff) = tempfile($self->get_basename(1) . '.diff.XXXXXX', + DIR => File::Spec->tmpdir(), UNLINK => 0); + push_exit_handler(sub { unlink($tmpdiff) }); + my $diff = Dpkg::Source::Patch->new(filename => $tmpdiff, + compression => 'none'); + $diff->create(); + if ($opts{header_from} and -e $opts{header_from}) { + my $header_from = Dpkg::Source::Patch->new( + filename => $opts{header_from}); + my $analysis = $header_from->analyze($dir, verbose => 0); + $diff->set_header($analysis->{patchheader}); + } else { + $diff->set_header($self->get_patch_header($dir)); + } + $diff->add_diff_directory($tmp, $dir, basedirname => $basedirname, + %{$self->{diff_options}}, + handle_binary_func => $opts{handle_binary}, + order_from => $opts{order_from}); + error(_g('unrepresentable changes to source')) if not $diff->finish(); + + if (-s $tmpdiff) { + info(_g('local changes detected, the modified files are:')); + my $analysis = $diff->analyze($dir, verbose => 0); + foreach my $fn (sort keys %{$analysis->{filepatched}}) { + print " $fn\n"; + } + } + + # Remove the temporary directory + erasedir($tmp); + pop_exit_handler(); + pop_exit_handler(); + + return $tmpdiff; +} + +sub do_build { + my ($self, $dir) = @_; + my @argv = @{$self->{options}{ARGV}}; + if (scalar(@argv)) { + usageerr(_g("-b takes only one parameter with format `%s'"), + $self->{fields}{'Format'}); + } + $self->prepare_build($dir); + + my $include_binaries = $self->{options}{include_binaries}; + my @tar_ignore = map { "--exclude=$_" } @{$self->{options}{tar_ignore}}; + + my $sourcepackage = $self->{fields}{'Source'}; + my $basenamerev = $self->get_basename(1); + + # Check if the debian directory contains unwanted binary files + my $binaryfiles = Dpkg::Source::Package::V2::BinaryFiles->new($dir); + my $unwanted_binaries = 0; + my $check_binary = sub { + if (-f $_ and is_binary($_)) { + my $fn = File::Spec->abs2rel($_, $dir); + $binaryfiles->new_binary_found($fn); + unless ($include_binaries or $binaryfiles->binary_is_allowed($fn)) { + errormsg(_g('unwanted binary file: %s'), $fn); + $unwanted_binaries++; + } + } + }; + my $tar_ignore_glob = '{' . join(',', + map { + my $copy = $_; + $copy =~ s/,/\\,/g; + $copy; + } @{$self->{options}{tar_ignore}}) . '}'; + my $filter_ignore = sub { + # Filter out files that are not going to be included in the debian + # tarball due to ignores. + my %exclude; + my $reldir = File::Spec->abs2rel($File::Find::dir, $dir); + my $cwd = getcwd(); + # Apply the pattern both from the top dir and from the inspected dir + chdir($dir) or syserr(_g("unable to chdir to `%s'"), $dir); + $exclude{$_} = 1 foreach glob($tar_ignore_glob); + chdir($cwd) or syserr(_g("unable to chdir to `%s'"), $cwd); + chdir($File::Find::dir) + or syserr(_g("unable to chdir to `%s'"), $File::Find::dir); + $exclude{$_} = 1 foreach glob($tar_ignore_glob); + chdir($cwd) or syserr(_g("unable to chdir to `%s'"), $cwd); + my @result; + foreach my $fn (@_) { + unless (exists $exclude{$fn} or exists $exclude{"$reldir/$fn"}) { + push @result, $fn; + } + } + return @result; + }; + find({ wanted => $check_binary, preprocess => $filter_ignore, + no_chdir => 1 }, File::Spec->catdir($dir, 'debian')); + error(P_('detected %d unwanted binary file (add it in ' . + 'debian/source/include-binaries to allow its inclusion).', + 'detected %d unwanted binary files (add them in ' . + 'debian/source/include-binaries to allow their inclusion).', + $unwanted_binaries), $unwanted_binaries) + if $unwanted_binaries; + + # Handle modified binary files detected by the auto-patch generation + my $handle_binary = sub { + my ($self, $old, $new) = @_; + my $relfn = File::Spec->abs2rel($new, $dir); + $binaryfiles->new_binary_found($relfn); + unless ($include_binaries or $binaryfiles->binary_is_allowed($relfn)) { + errormsg(_g('cannot represent change to %s: %s'), $relfn, + _g('binary file contents changed')); + errormsg(_g('add %s in debian/source/include-binaries if you want ' . + 'to store the modified binary in the debian tarball'), + $relfn); + $self->register_error(); + } + }; + + # Create a patch + my $autopatch = File::Spec->catfile($dir, 'debian', 'patches', + $self->get_autopatch_name()); + my $tmpdiff = $self->generate_patch($dir, order_from => $autopatch, + header_from => $autopatch, + handle_binary => $handle_binary, + skip_auto => $self->{options}{auto_commit}, + usage => 'build'); + unless (-z $tmpdiff or $self->{options}{auto_commit}) { + info(_g('you can integrate the local changes with %s'), + 'dpkg-source --commit'); + error(_g('aborting due to unexpected upstream changes, see %s'), + $tmpdiff); + } + push_exit_handler(sub { unlink($tmpdiff) }); + $binaryfiles->update_debian_source_include_binaries() if $include_binaries; + + # Install the diff as the new autopatch + if ($self->{options}{auto_commit}) { + mkpath(File::Spec->catdir($dir, 'debian', 'patches')); + $autopatch = $self->register_patch($dir, $tmpdiff, + $self->get_autopatch_name()); + info(_g('local changes have been recorded in a new patch: %s'), + $autopatch) if -e $autopatch; + rmdir(File::Spec->catdir($dir, 'debian', 'patches')); # No check on purpose + } + unlink($tmpdiff) or syserr(_g('cannot remove %s'), $tmpdiff); + pop_exit_handler(); + + # Create the debian.tar + my $debianfile = "$basenamerev.debian.tar." . $self->{options}{comp_ext}; + info(_g('building %s in %s'), $sourcepackage, $debianfile); + my $tar = Dpkg::Source::Archive->new(filename => $debianfile); + $tar->create(options => \@tar_ignore, chdir => $dir); + $tar->add_directory('debian'); + foreach my $binary ($binaryfiles->get_seen_binaries()) { + $tar->add_file($binary) unless $binary =~ m{^debian/}; + } + $tar->finish(); + + $self->add_file($debianfile); +} + +sub get_patch_header { + my ($self, $dir) = @_; + my $ph = File::Spec->catfile($dir, 'debian', 'source', 'local-patch-header'); + unless (-f $ph) { + $ph = File::Spec->catfile($dir, 'debian', 'source', 'patch-header'); + } + my $text; + if (-f $ph) { + open(my $ph_fh, '<', $ph) or syserr(_g('cannot read %s'), $ph); + $text = file_slurp($ph_fh); + close($ph_fh); + return $text; + } + my $ch_info = changelog_parse(offset => 0, count => 1, + file => File::Spec->catfile($dir, 'debian', 'changelog')); + return '' if not defined $ch_info; + my $header = Dpkg::Control->new(type => CTRL_UNKNOWN); + $header->{'Description'} = "<short summary of the patch>\n"; + $header->{'Description'} .= +"TODO: Put a short summary on the line above and replace this paragraph +with a longer explanation of this change. Complete the meta-information +with other relevant fields (see below for details). To make it easier, the +information below has been extracted from the changelog. Adjust it or drop +it.\n"; + $header->{'Description'} .= $ch_info->{'Changes'} . "\n"; + $header->{'Author'} = $ch_info->{'Maintainer'}; + $text = "$header"; + run_vendor_hook('extend-patch-header', \$text, $ch_info); + $text .= "\n--- +The information above should follow the Patch Tagging Guidelines, please +checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here +are templates for supplementary fields that you might want to add: + +Origin: <vendor|upstream|other>, <url of original patch> +Bug: <url in upstream bugtracker> +Bug-Debian: http://bugs.debian.org/<bugnumber> +Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber> +Forwarded: <no|not-needed|url proving that it has been forwarded> +Reviewed-By: <name and email of someone who approved the patch> +Last-Update: <YYYY-MM-DD>\n\n"; + return $text; +} + +sub register_patch { + my ($self, $dir, $patch_file, $patch_name) = @_; + my $patch = File::Spec->catfile($dir, 'debian', 'patches', $patch_name); + if (-s $patch_file) { + copy($patch_file, $patch) + or syserr(_g('failed to copy %s to %s'), $patch_file, $patch); + chmod(0666 & ~ umask(), $patch) + or syserr(_g("unable to change permission of `%s'"), $patch); + my $applied = File::Spec->catfile($dir, 'debian', 'patches', '.dpkg-source-applied'); + open(my $applied_fh, '>>', $applied) + or syserr(_g('cannot write %s'), $applied); + print { $applied_fh } "$patch\n"; + close($applied_fh) or syserr(_g('cannot close %s'), $applied); + } elsif (-e $patch) { + unlink($patch) or syserr(_g('cannot remove %s'), $patch); + } + return $patch; +} + +sub _is_bad_patch_name { + my ($dir, $patch_name) = @_; + + return 1 if not defined($patch_name); + return 1 if not length($patch_name); + + my $patch = File::Spec->catfile($dir, 'debian', 'patches', $patch_name); + if (-e $patch) { + warning(_g('cannot register changes in %s, this patch already exists'), + $patch); + return 1; + } + return 0; +} + +sub do_commit { + my ($self, $dir) = @_; + my ($patch_name, $tmpdiff) = @{$self->{options}{ARGV}}; + + $self->prepare_build($dir); + + # Try to fix up a broken relative filename for the patch + if ($tmpdiff and not -e $tmpdiff) { + $tmpdiff = File::Spec->catfile($dir, $tmpdiff) + unless File::Spec->file_name_is_absolute($tmpdiff); + error(_g("patch file '%s' doesn't exist"), $tmpdiff) if not -e $tmpdiff; + } + + my $binaryfiles = Dpkg::Source::Package::V2::BinaryFiles->new($dir); + my $handle_binary = sub { + my ($self, $old, $new) = @_; + my $fn = File::Spec->abs2rel($new, $dir); + $binaryfiles->new_binary_found($fn); + }; + + unless ($tmpdiff) { + $tmpdiff = $self->generate_patch($dir, handle_binary => $handle_binary, + usage => 'commit'); + $binaryfiles->update_debian_source_include_binaries(); + } + push_exit_handler(sub { unlink($tmpdiff) }); + unless (-s $tmpdiff) { + unlink($tmpdiff) or syserr(_g('cannot remove %s'), $tmpdiff); + info(_g('there are no local changes to record')); + return; + } + while (_is_bad_patch_name($dir, $patch_name)) { + # Ask the patch name interactively + print _g('Enter the desired patch name: '); + chomp($patch_name = <STDIN>); + $patch_name =~ s/\s+/-/g; + $patch_name =~ s/\///g; + } + mkpath(File::Spec->catdir($dir, 'debian', 'patches')); + my $patch = $self->register_patch($dir, $tmpdiff, $patch_name); + system('sensible-editor', $patch); + subprocerr('sensible-editor') if $?; + unlink($tmpdiff) or syserr(_g('cannot remove %s'), $tmpdiff); + pop_exit_handler(); + info(_g('local changes have been recorded in a new patch: %s'), $patch); +} + +package Dpkg::Source::Package::V2::BinaryFiles; + +use Dpkg::ErrorHandling; +use Dpkg::Gettext; + +use File::Path; + +sub new { + my ($this, $dir) = @_; + my $class = ref($this) || $this; + + my $self = { + dir => $dir, + allowed_binaries => {}, + seen_binaries => {}, + include_binaries_path => + File::Spec->catfile($dir, 'debian', 'source', 'include-binaries'), + }; + bless $self, $class; + $self->load_allowed_binaries(); + return $self; +} + +sub new_binary_found { + my ($self, $path) = @_; + + $self->{seen_binaries}{$path} = 1; +} + +sub load_allowed_binaries { + my ($self) = @_; + my $incbin_file = $self->{include_binaries_path}; + if (-f $incbin_file) { + open(my $incbin_fh, '<', $incbin_file) + or syserr(_g('cannot read %s'), $incbin_file); + while (defined($_ = <$incbin_fh>)) { + chomp; s/^\s*//; s/\s*$//; + next if /^#/ or /^$/; + $self->{allowed_binaries}{$_} = 1; + } + close($incbin_fh); + } +} + +sub binary_is_allowed { + my ($self, $path) = @_; + return 1 if exists $self->{allowed_binaries}{$path}; + return 0; +} + +sub update_debian_source_include_binaries { + my ($self) = @_; + + my @unknown_binaries = $self->get_unknown_binaries(); + return unless scalar(@unknown_binaries); + + my $incbin_file = $self->{include_binaries_path}; + mkpath(File::Spec->catdir($self->{dir}, 'debian', 'source')); + open(my $incbin_fh, '>>', $incbin_file) + or syserr(_g('cannot write %s'), $incbin_file); + foreach my $binary (@unknown_binaries) { + print { $incbin_fh } "$binary\n"; + info(_g('adding %s to %s'), $binary, 'debian/source/include-binaries'); + $self->{allowed_binaries}{$binary} = 1; + } + close($incbin_fh); +} + +sub get_unknown_binaries { + my ($self) = @_; + return grep { not $self->binary_is_allowed($_) } $self->get_seen_binaries(); +} + +sub get_seen_binaries { + my ($self) = @_; + my @seen = sort keys %{$self->{seen_binaries}}; + return @seen; +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V3/Bzr.pm b/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V3/Bzr.pm new file mode 100644 index 0000000..1bf4eb9 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V3/Bzr.pm
@@ -0,0 +1,210 @@ +# +# bzr support for dpkg-source +# +# Copyright © 2007 Colin Watson <cjwatson@debian.org>. +# Based on Dpkg::Source::Package::V3_0::git, which is: +# Copyright © 2007 Joey Hess <joeyh@debian.org>. +# Copyright © 2008 Frank Lichtenheld <djpig@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Source::Package::V3::Bzr; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use parent qw(Dpkg::Source::Package); + +use Cwd; +use File::Basename; +use File::Find; +use File::Temp qw(tempdir); + +use Dpkg; +use Dpkg::Gettext; +use Dpkg::Compression; +use Dpkg::ErrorHandling; +use Dpkg::Source::Archive; +use Dpkg::Exit qw(push_exit_handler pop_exit_handler); +use Dpkg::Source::Functions qw(erasedir); + +our $CURRENT_MINOR_VERSION = '0'; + +sub import { + foreach my $dir (split(/:/, $ENV{PATH})) { + if (-x "$dir/bzr") { + return 1; + } + } + error(_g('cannot unpack bzr-format source package because ' . + 'bzr is not in the PATH')); +} + +sub sanity_check { + my $srcdir = shift; + + if (! -d "$srcdir/.bzr") { + error(_g('source directory is not the top directory of a bzr repository (%s/.bzr not present), but Format bzr was specified'), + $srcdir); + } + + # Symlinks from .bzr to outside could cause unpack failures, or + # point to files they shouldn't, so check for and don't allow. + if (-l "$srcdir/.bzr") { + error(_g('%s is a symlink'), "$srcdir/.bzr"); + } + my $abs_srcdir = Cwd::abs_path($srcdir); + find(sub { + if (-l $_) { + if (Cwd::abs_path(readlink($_)) !~ /^\Q$abs_srcdir\E(\/|$)/) { + error(_g('%s is a symlink to outside %s'), + $File::Find::name, $srcdir); + } + } + }, "$srcdir/.bzr"); + + return 1; +} + +sub can_build { + my ($self, $dir) = @_; + + return (0, _g("doesn't contain a bzr repository")) unless -d "$dir/.bzr"; + return 1; +} + +sub do_build { + my ($self, $dir) = @_; + my @argv = @{$self->{options}{ARGV}}; + # TODO: warn here? + #my @tar_ignore = map { "--exclude=$_" } @{$self->{options}{tar_ignore}}; + my $diff_ignore_regex = $self->{options}{diff_ignore_regex}; + + $dir =~ s{/+$}{}; # Strip trailing / + my ($dirname, $updir) = fileparse($dir); + + if (scalar(@argv)) { + usageerr(_g("-b takes only one parameter with format `%s'"), + $self->{fields}{'Format'}); + } + + my $sourcepackage = $self->{fields}{'Source'}; + my $basenamerev = $self->get_basename(1); + my $basename = $self->get_basename(); + my $basedirname = $basename; + $basedirname =~ s/_/-/; + + sanity_check($dir); + + my $old_cwd = getcwd(); + chdir($dir) or syserr(_g("unable to chdir to `%s'"), $dir); + + # Check for uncommitted files. + # To support dpkg-source -i, remove any ignored files from the + # output of bzr status. + open(my $bzr_status_fh, '-|', 'bzr', 'status') + or subprocerr('bzr status'); + my @files; + while (<$bzr_status_fh>) { + chomp; + next unless s/^ +//; + if (! length $diff_ignore_regex || + ! m/$diff_ignore_regex/o) { + push @files, $_; + } + } + close($bzr_status_fh) or syserr(_g('bzr status exited nonzero')); + if (@files) { + error(_g('uncommitted, not-ignored changes in working directory: %s'), + join(' ', @files)); + } + + chdir($old_cwd) or syserr(_g("unable to chdir to `%s'"), $old_cwd); + + my $tmp = tempdir("$dirname.bzr.XXXXXX", DIR => $updir); + push_exit_handler(sub { erasedir($tmp) }); + my $tardir = "$tmp/$dirname"; + + system('bzr', 'branch', $dir, $tardir); + subprocerr("bzr branch $dir $tardir") if $?; + + # Remove the working tree. + system('bzr', 'remove-tree', $tardir); + subprocerr("bzr remove-tree $tardir") if $?; + + # Some branch metadata files are unhelpful. + unlink("$tardir/.bzr/branch/branch-name", + "$tardir/.bzr/branch/parent"); + + # Create the tar file + my $debianfile = "$basenamerev.bzr.tar." . $self->{options}{comp_ext}; + info(_g('building %s in %s'), + $sourcepackage, $debianfile); + my $tar = Dpkg::Source::Archive->new(filename => $debianfile, + compression => $self->{options}{compression}, + compression_level => $self->{options}{comp_level}); + $tar->create(chdir => $tmp); + $tar->add_directory($dirname); + $tar->finish(); + + erasedir($tmp); + pop_exit_handler(); + + $self->add_file($debianfile); +} + +# Called after a tarball is unpacked, to check out the working copy. +sub do_extract { + my ($self, $newdirectory) = @_; + my $fields = $self->{fields}; + + my $dscdir = $self->{basedir}; + + my $basename = $self->get_basename(); + my $basenamerev = $self->get_basename(1); + + my @files = $self->get_files(); + if (@files > 1) { + error(_g('format v3.0 uses only one source file')); + } + my $tarfile = $files[0]; + my $comp_ext_regex = compression_get_file_extension_regex(); + if ($tarfile !~ /^\Q$basenamerev\E\.bzr\.tar\.$comp_ext_regex$/) { + error(_g('expected %s, got %s'), + "$basenamerev.bzr.tar.$comp_ext_regex", $tarfile); + } + + erasedir($newdirectory); + + # Extract main tarball + info(_g('unpacking %s'), $tarfile); + my $tar = Dpkg::Source::Archive->new(filename => "$dscdir$tarfile"); + $tar->extract($newdirectory); + + sanity_check($newdirectory); + + my $old_cwd = getcwd(); + chdir($newdirectory) + or syserr(_g("unable to chdir to `%s'"), $newdirectory); + + # Reconstitute the working tree. + system('bzr', 'checkout'); + subprocerr('bzr checkout') if $?; + + chdir($old_cwd) or syserr(_g("unable to chdir to `%s'"), $old_cwd); +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V3/Custom.pm b/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V3/Custom.pm new file mode 100644 index 0000000..3ea9d05f --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V3/Custom.pm
@@ -0,0 +1,63 @@ +# Copyright © 2008 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Source::Package::V3::Custom; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use parent qw(Dpkg::Source::Package); + +use Dpkg; +use Dpkg::Gettext; +use Dpkg::ErrorHandling; + +our $CURRENT_MINOR_VERSION = '0'; + +sub parse_cmdline_option { + my ($self, $opt) = @_; + if ($opt =~ /^--target-format=(.*)$/) { + $self->{options}{target_format} = $1; + return 1; + } + return 0; +} +sub do_extract { + error(_g("Format `3.0 (custom)' is only used to create source packages")); +} + +sub can_build { + my ($self, $dir) = @_; + + return (0, _g('no files indicated on command line')) + unless scalar(@{$self->{options}{ARGV}}); + return 1; +} + +sub do_build { + my ($self, $dir) = @_; + # Update real target format + my $format = $self->{options}{target_format}; + error(_g('--target-format option is missing')) unless $format; + $self->{fields}{'Format'} = $format; + # Add all files + foreach my $file (@{$self->{options}{ARGV}}) { + $self->add_file($file); + } +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V3/Git.pm b/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V3/Git.pm new file mode 100644 index 0000000..21eb667 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V3/Git.pm
@@ -0,0 +1,235 @@ +# +# git support for dpkg-source +# +# Copyright © 2007,2010 Joey Hess <joeyh@debian.org>. +# Copyright © 2008 Frank Lichtenheld <djpig@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Source::Package::V3::Git; + +use strict; +use warnings; + +our $VERSION = '0.02'; + +use parent qw(Dpkg::Source::Package); + +use Cwd qw(abs_path getcwd); +use File::Basename; +use File::Temp qw(tempdir); + +use Dpkg; +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Exit qw(push_exit_handler pop_exit_handler); +use Dpkg::Source::Functions qw(erasedir); + +our $CURRENT_MINOR_VERSION = '0'; + +# Remove variables from the environment that might cause git to do +# something unexpected. +delete $ENV{GIT_DIR}; +delete $ENV{GIT_INDEX_FILE}; +delete $ENV{GIT_OBJECT_DIRECTORY}; +delete $ENV{GIT_ALTERNATE_OBJECT_DIRECTORIES}; +delete $ENV{GIT_WORK_TREE}; + +sub import { + foreach my $dir (split(/:/, $ENV{PATH})) { + if (-x "$dir/git") { + return 1; + } + } + error(_g('cannot unpack git-format source package because ' . + 'git is not in the PATH')); +} + +sub sanity_check { + my $srcdir = shift; + + if (! -d "$srcdir/.git") { + error(_g('source directory is not the top directory of a git ' . + 'repository (%s/.git not present), but Format git was ' . + 'specified'), $srcdir); + } + if (-s "$srcdir/.gitmodules") { + error(_g('git repository %s uses submodules; this is not yet supported'), + $srcdir); + } + + return 1; +} + +sub parse_cmdline_option { + my ($self, $opt) = @_; + return 1 if $self->SUPER::parse_cmdline_option($opt); + if ($opt =~ /^--git-ref=(.*)$/) { + push @{$self->{options}{git_ref}}, $1; + return 1; + } elsif ($opt =~ /^--git-depth=(\d+)$/) { + $self->{options}{git_depth} = $1; + return 1; + } + return 0; +} + +sub can_build { + my ($self, $dir) = @_; + + return (0, _g("doesn't contain a git repository")) unless -d "$dir/.git"; + return 1; +} + +sub do_build { + my ($self, $dir) = @_; + my $diff_ignore_regex = $self->{options}{diff_ignore_regex}; + + $dir =~ s{/+$}{}; # Strip trailing / + my ($dirname, $updir) = fileparse($dir); + my $basenamerev = $self->get_basename(1); + + sanity_check($dir); + + my $old_cwd = getcwd(); + chdir($dir) or syserr(_g("unable to chdir to `%s'"), $dir); + + # Check for uncommitted files. + # To support dpkg-source -i, get a list of files + # equivalent to the ones git status finds, and remove any + # ignored files from it. + my @ignores = '--exclude-per-directory=.gitignore'; + my $core_excludesfile = `git config --get core.excludesfile`; + chomp $core_excludesfile; + if (length $core_excludesfile && -e $core_excludesfile) { + push @ignores, "--exclude-from=$core_excludesfile"; + } + if (-e '.git/info/exclude') { + push @ignores, '--exclude-from=.git/info/exclude'; + } + open(my $git_ls_files_fh, '-|', 'git', 'ls-files', '--modified', '--deleted', + '-z', '--others', @ignores) or subprocerr('git ls-files'); + my @files; + { local $/ = "\0"; + while (<$git_ls_files_fh>) { + chomp; + if (! length $diff_ignore_regex || + ! m/$diff_ignore_regex/o) { + push @files, $_; + } + } + } + close($git_ls_files_fh) or syserr(_g('git ls-files exited nonzero')); + if (@files) { + error(_g('uncommitted, not-ignored changes in working directory: %s'), + join(' ', @files)); + } + + # If a depth was specified, need to create a shallow clone and + # bundle that. + my $tmp; + my $shallowfile; + if ($self->{options}{git_depth}) { + chdir($old_cwd) or syserr(_g("unable to chdir to `%s'"), $old_cwd); + $tmp = tempdir("$dirname.git.XXXXXX", DIR => $updir); + push_exit_handler(sub { erasedir($tmp) }); + my $clone_dir = "$tmp/repo.git"; + # file:// is needed to avoid local cloning, which does not + # create a shallow clone. + info(_g('creating shallow clone with depth %s'), + $self->{options}{git_depth}); + system('git', 'clone', '--depth=' . $self->{options}{git_depth}, + '--quiet', '--bare', 'file://' . abs_path($dir), $clone_dir); + subprocerr('git clone') if $?; + chdir($clone_dir) + or syserr(_g("unable to chdir to `%s'"), $clone_dir); + $shallowfile = "$basenamerev.gitshallow"; + system('cp', '-f', 'shallow', "$old_cwd/$shallowfile"); + subprocerr('cp shallow') if $?; + } + + # Create the git bundle. + my $bundlefile = "$basenamerev.git"; + my @bundle_arg=$self->{options}{git_ref} ? + (@{$self->{options}{git_ref}}) : '--all'; + info(_g('bundling: %s'), join(' ', @bundle_arg)); + system('git', 'bundle', 'create', "$old_cwd/$bundlefile", + @bundle_arg, + 'HEAD', # ensure HEAD is included no matter what + '--', # avoids ambiguity error when referring to eg, a debian branch + ); + subprocerr('git bundle') if $?; + + chdir($old_cwd) or syserr(_g("unable to chdir to `%s'"), $old_cwd); + + if (defined $tmp) { + erasedir($tmp); + pop_exit_handler(); + } + + $self->add_file($bundlefile); + if (defined $shallowfile) { + $self->add_file($shallowfile); + } +} + +sub do_extract { + my ($self, $newdirectory) = @_; + my $fields = $self->{fields}; + + my $dscdir = $self->{basedir}; + my $basenamerev = $self->get_basename(1); + + my @files = $self->get_files(); + my ($bundle, $shallow); + foreach my $file (@files) { + if ($file =~ /^\Q$basenamerev\E\.git$/) { + if (! defined $bundle) { + $bundle = $file; + } else { + error(_g('format v3.0 (git) uses only one .git file')); + } + } elsif ($file =~ /^\Q$basenamerev\E\.gitshallow$/) { + if (! defined $shallow) { + $shallow = $file; + } else { + error(_g('format v3.0 (git) uses only one .gitshallow file')); + } + } else { + error(_g('format v3.0 (git) unknown file: %s', $file)); + } + } + if (! defined $bundle) { + error(_g('format v3.0 (git) expected %s'), "$basenamerev.git"); + } + + erasedir($newdirectory); + + # Extract git bundle. + info(_g('cloning %s'), $bundle); + system('git', 'clone', '--quiet', $dscdir . $bundle, $newdirectory); + subprocerr('git bundle') if $?; + + if (defined $shallow) { + # Move shallow info file into place, so git does not + # try to follow parents of shallow refs. + info(_g('setting up shallow clone')); + system('cp', '-f', $dscdir . $shallow, "$newdirectory/.git/shallow"); + subprocerr('cp') if $?; + } + + sanity_check($newdirectory); +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V3/Native.pm b/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V3/Native.pm new file mode 100644 index 0000000..a50f2a7 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V3/Native.pm
@@ -0,0 +1,115 @@ +# Copyright © 2008 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Source::Package::V3::Native; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use parent qw(Dpkg::Source::Package); + +use Dpkg; +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Compression; +use Dpkg::Exit qw(push_exit_handler pop_exit_handler); +use Dpkg::Version; +use Dpkg::Source::Archive; +use Dpkg::Source::Functions qw(erasedir); + +use Cwd; +use File::Basename; +use File::Temp qw(tempfile); + +our $CURRENT_MINOR_VERSION = '0'; + +sub do_extract { + my ($self, $newdirectory) = @_; + my $sourcestyle = $self->{options}{sourcestyle}; + my $fields = $self->{fields}; + + my $dscdir = $self->{basedir}; + my $basename = $self->get_basename(); + my $basenamerev = $self->get_basename(1); + + my $tarfile; + my $comp_ext_regex = compression_get_file_extension_regex(); + foreach my $file ($self->get_files()) { + if ($file =~ /^\Q$basenamerev\E\.tar\.$comp_ext_regex$/) { + error(_g('multiple tarfiles in v1.0 source package')) if $tarfile; + $tarfile = $file; + } else { + error(_g('unrecognized file for a native source package: %s'), $file); + } + } + + error(_g('no tarfile in Files field')) unless $tarfile; + + erasedir($newdirectory); + info(_g('unpacking %s'), $tarfile); + my $tar = Dpkg::Source::Archive->new(filename => "$dscdir$tarfile"); + $tar->extract($newdirectory); +} + +sub can_build { + my ($self, $dir) = @_; + + my $v = Dpkg::Version->new($self->{fields}->{'Version'}); + warning (_g('native package version may not have a revision')) + unless $v->is_native(); + + return 1; +} + +sub do_build { + my ($self, $dir) = @_; + my @tar_ignore = map { "--exclude=$_" } @{$self->{options}{tar_ignore}}; + my @argv = @{$self->{options}{ARGV}}; + + if (scalar(@argv)) { + usageerr(_g("-b takes only one parameter with format `%s'"), + $self->{fields}{'Format'}); + } + + my $sourcepackage = $self->{fields}{'Source'}; + my $basenamerev = $self->get_basename(1); + my $tarname = "$basenamerev.tar." . $self->{options}{comp_ext}; + + info(_g('building %s in %s'), $sourcepackage, $tarname); + + my ($ntfh, $newtar) = tempfile("$tarname.new.XXXXXX", + DIR => getcwd(), UNLINK => 0); + push_exit_handler(sub { unlink($newtar) }); + + my ($dirname, $dirbase) = fileparse($dir); + my $tar = Dpkg::Source::Archive->new(filename => $newtar, + compression => compression_guess_from_filename($tarname), + compression_level => $self->{options}{comp_level}); + $tar->create(options => \@tar_ignore, chdir => $dirbase); + $tar->add_directory($dirname); + $tar->finish(); + rename($newtar, $tarname) + or syserr(_g("unable to rename `%s' (newly created) to `%s'"), + $newtar, $tarname); + pop_exit_handler(); + chmod(0666 &~ umask(), $tarname) + or syserr(_g("unable to change permission of `%s'"), $tarname); + + $self->add_file($tarname); +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V3/Quilt.pm b/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V3/Quilt.pm new file mode 100644 index 0000000..6feb3f67 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Source/Package/V3/Quilt.pm
@@ -0,0 +1,291 @@ +# Copyright © 2008-2012 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Source::Package::V3::Quilt; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +# Based on wig&pen implementation +use parent qw(Dpkg::Source::Package::V2); + +use Dpkg; +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Util qw(:list); +use Dpkg::Version; +use Dpkg::Source::Patch; +use Dpkg::Source::Functions qw(erasedir fs_time); +use Dpkg::Source::Quilt; +use Dpkg::Exit; + +use File::Spec; +use File::Copy; + +our $CURRENT_MINOR_VERSION = '0'; + +sub init_options { + my ($self) = @_; + $self->{options}{single_debian_patch} = 0 + unless exists $self->{options}{single_debian_patch}; + $self->{options}{allow_version_of_quilt_db} = [] + unless exists $self->{options}{allow_version_of_quilt_db}; + + $self->SUPER::init_options(); +} + +sub parse_cmdline_option { + my ($self, $opt) = @_; + return 1 if $self->SUPER::parse_cmdline_option($opt); + if ($opt =~ /^--single-debian-patch$/) { + $self->{options}{single_debian_patch} = 1; + # For backwards compatibility. + $self->{options}{auto_commit} = 1; + return 1; + } elsif ($opt =~ /^--allow-version-of-quilt-db=(.*)$/) { + push @{$self->{options}{allow_version_of_quilt_db}}, $1; + return 1; + } + return 0; +} + +sub build_quilt_object { + my ($self, $dir) = @_; + return $self->{quilt}{$dir} if exists $self->{quilt}{$dir}; + $self->{quilt}{$dir} = Dpkg::Source::Quilt->new($dir); + return $self->{quilt}{$dir}; +} + +sub can_build { + my ($self, $dir) = @_; + my ($code, $msg) = $self->SUPER::can_build($dir); + return ($code, $msg) if $code == 0; + + my $v = Dpkg::Version->new($self->{fields}->{'Version'}); + warning (_g('version does not contain a revision')) if $v->is_native(); + + my $quilt = $self->build_quilt_object($dir); + $msg = $quilt->find_problems(); + return (0, $msg) if $msg; + return 1; +} + +sub get_autopatch_name { + my ($self) = @_; + if ($self->{options}{single_debian_patch}) { + return 'debian-changes'; + } else { + return 'debian-changes-' . $self->{fields}{'Version'}; + } +} + +sub apply_patches { + my ($self, $dir, %opts) = @_; + + if ($opts{usage} eq 'unpack') { + $opts{verbose} = 1; + } elsif ($opts{usage} eq 'build') { + $opts{warn_options} = 1; + $opts{verbose} = 0; + } + + my $quilt = $self->build_quilt_object($dir); + $quilt->load_series(%opts) if $opts{warn_options}; # Trigger warnings + + # Always create the quilt db so that if the maintainer calls quilt to + # create a patch, it's stored in the right directory + $quilt->write_db(); + + # Update debian/patches/series symlink if needed to allow quilt usage + my $series = $quilt->get_series_file(); + my $basename = (File::Spec->splitpath($series))[2]; + if ($basename ne 'series') { + my $dest = $quilt->get_patch_file('series'); + unlink($dest) if -l $dest; + unless (-f _) { # Don't overwrite real files + symlink($basename, $dest) + or syserr(_g("can't create symlink %s"), $dest); + } + } + + return unless scalar($quilt->series()); + + if ($opts{usage} eq 'preparation' and + $self->{options}{unapply_patches} eq 'auto') { + # We're applying the patches in --before-build, remember to unapply + # them afterwards in --after-build + my $pc_unapply = $quilt->get_db_file('.dpkg-source-unapply'); + open(my $unapply_fh, '>', $pc_unapply) + or syserr(_g('cannot write %s'), $pc_unapply); + close($unapply_fh); + } + + # Apply patches + my $pc_applied = $quilt->get_db_file('applied-patches'); + $opts{timestamp} = fs_time($pc_applied); + if ($opts{skip_auto}) { + my $auto_patch = $self->get_autopatch_name(); + $quilt->push(%opts) while ($quilt->next() and $quilt->next() ne $auto_patch); + } else { + $quilt->push(%opts) while $quilt->next(); + } +} + +sub unapply_patches { + my ($self, $dir, %opts) = @_; + + my $quilt = $self->build_quilt_object($dir); + + $opts{verbose} //= 1; + + my $pc_applied = $quilt->get_db_file('applied-patches'); + my @applied = $quilt->applied(); + $opts{timestamp} = fs_time($pc_applied) if @applied; + + $quilt->pop(%opts) while $quilt->top(); + + erasedir($quilt->get_db_dir()); +} + +sub prepare_build { + my ($self, $dir) = @_; + $self->SUPER::prepare_build($dir); + # Skip .pc directories of quilt by default and ignore difference + # on debian/patches/series symlinks and d/p/.dpkg-source-applied + # stamp file created by ourselves + my $func = sub { + return 1 if $_[0] =~ m{^debian/patches/series$} and -l $_[0]; + return 1 if $_[0] =~ /^\.pc(\/|$)/; + return 1 if $_[0] =~ /$self->{options}{diff_ignore_regex}/; + return 0; + }; + $self->{diff_options}{diff_ignore_func} = $func; +} + +sub do_build { + my ($self, $dir) = @_; + + my $quilt = $self->build_quilt_object($dir); + my $version = $quilt->get_db_version(); + + if (defined($version) and $version != 2) { + if (any { $version eq $_ } + @{$self->{options}{allow_version_of_quilt_db}}) + { + warning(_g('unsupported version of the quilt metadata: %s'), $version); + } else { + error(_g('unsupported version of the quilt metadata: %s'), $version); + } + } + + $self->SUPER::do_build($dir); +} + +sub after_build { + my ($self, $dir) = @_; + my $quilt = $self->build_quilt_object($dir); + my $pc_unapply = $quilt->get_db_file('.dpkg-source-unapply'); + my $opt_unapply = $self->{options}{unapply_patches}; + if (($opt_unapply eq 'auto' and -e $pc_unapply) or $opt_unapply eq 'yes') { + unlink($pc_unapply); + $self->unapply_patches($dir); + } +} + +sub check_patches_applied { + my ($self, $dir) = @_; + + my $quilt = $self->build_quilt_object($dir); + my $next = $quilt->next(); + return if not defined $next; + + my $first_patch = File::Spec->catfile($dir, 'debian', 'patches', $next); + my $patch_obj = Dpkg::Source::Patch->new(filename => $first_patch); + return unless $patch_obj->check_apply($dir); + + $self->apply_patches($dir, usage => 'preparation', verbose => 1); +} + +sub _add_line { + my ($file, $line) = @_; + + open(my $file_fh, '>>', $file) or syserr(_g('cannot write %s'), $file); + print { $file_fh } "$line\n"; + close($file_fh); +} + +sub _drop_line { + my ($file, $re) = @_; + + open(my $file_fh, '<', $file) or syserr(_g('cannot read %s'), $file); + my @lines = <$file_fh>; + close($file_fh); + open($file_fh, '>', $file) or syserr(_g('cannot write %s'), $file); + print { $file_fh } $_ foreach grep { not /^\Q$re\E\s*$/ } @lines; + close($file_fh); +} + +sub register_patch { + my ($self, $dir, $tmpdiff, $patch_name) = @_; + + my $quilt = $self->build_quilt_object($dir); + + my @patches = $quilt->series(); + my $has_patch = (grep { $_ eq $patch_name } @patches) ? 1 : 0; + my $series = $quilt->get_series_file(); + my $applied = $quilt->get_db_file('applied-patches'); + my $patch = $quilt->get_patch_file($patch_name); + + if (-s $tmpdiff) { + copy($tmpdiff, $patch) + or syserr(_g('failed to copy %s to %s'), $tmpdiff, $patch); + chmod(0666 & ~ umask(), $patch) + or syserr(_g("unable to change permission of `%s'"), $patch); + } elsif (-e $patch) { + unlink($patch) or syserr(_g('cannot remove %s'), $patch); + } + + if (-e $patch) { + $quilt->setup_db(); + # Add patch to series file + if (not $has_patch) { + _add_line($series, $patch_name); + _add_line($applied, $patch_name); + $quilt->load_series(); + $quilt->load_db(); + } + # Ensure quilt meta-data are created and in sync with some trickery: + # reverse-apply the patch, drop .pc/$patch, re-apply it + # with the correct options to recreate the backup files + $quilt->pop(reverse_apply => 1); + $quilt->push(); + } else { + # Remove auto_patch from series + if ($has_patch) { + _drop_line($series, $patch_name); + _drop_line($applied, $patch_name); + erasedir($quilt->get_db_file($patch_name)); + $quilt->load_db(); + $quilt->load_series(); + } + # Clean up empty series + unlink($series) if -z $series; + } + return $patch; +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Source/Patch.pm b/third_party/dpkg-dev/scripts/Dpkg/Source/Patch.pm new file mode 100644 index 0000000..a3fb846 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Source/Patch.pm
@@ -0,0 +1,640 @@ +# Copyright © 2008 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Source::Patch; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use Dpkg; +use Dpkg::Gettext; +use Dpkg::IPC; +use Dpkg::ErrorHandling; +use Dpkg::Source::Functions qw(fs_time); + +use POSIX qw(:errno_h :sys_wait_h); +use File::Find; +use File::Basename; +use File::Spec; +use File::Path; +use File::Compare; +use Fcntl ':mode'; +#XXX: Needed for sub-second timestamps, require recent perl +#use Time::HiRes qw(stat); + +use parent qw(Dpkg::Compression::FileHandle); + +sub create { + my ($self, %opts) = @_; + $self->ensure_open('w'); # Creates the file + *$self->{errors} = 0; + *$self->{empty} = 1; + if ($opts{old} and $opts{new}) { + $opts{old} = '/dev/null' unless -e $opts{old}; + $opts{new} = '/dev/null' unless -e $opts{new}; + if (-d $opts{old} and -d $opts{new}) { + $self->add_diff_directory($opts{old}, $opts{new}, %opts); + } elsif (-f $opts{old} and -f $opts{new}) { + $self->add_diff_file($opts{old}, $opts{new}, %opts); + } else { + $self->_fail_not_same_type($opts{old}, $opts{new}); + } + $self->finish() unless $opts{nofinish}; + } +} + +sub set_header { + my ($self, $header) = @_; + *$self->{header} = $header; +} + +sub add_diff_file { + my ($self, $old, $new, %opts) = @_; + $opts{include_timestamp} = 0 unless exists $opts{include_timestamp}; + my $handle_binary = $opts{handle_binary_func} || sub { + my ($self, $old, $new) = @_; + $self->_fail_with_msg($new, _g('binary file contents changed')); + }; + # Optimization to avoid forking diff if unnecessary + return 1 if compare($old, $new, 4096) == 0; + # Default diff options + my @options; + if ($opts{options}) { + push @options, @{$opts{options}}; + } else { + push @options, '-p'; + } + # Add labels + if ($opts{label_old} and $opts{label_new}) { + if ($opts{include_timestamp}) { + my $ts = (stat($old))[9]; + my $t = POSIX::strftime('%Y-%m-%d %H:%M:%S', gmtime($ts)); + $opts{label_old} .= sprintf("\t%s.%09d +0000", $t, + ($ts - int($ts)) * 1_000_000_000); + $ts = (stat($new))[9]; + $t = POSIX::strftime('%Y-%m-%d %H:%M:%S', gmtime($ts)); + $opts{label_new} .= sprintf("\t%s.%09d +0000", $t, + ($ts - int($ts)) * 1_000_000_000); + } else { + # Space in filenames need special treatment + $opts{label_old} .= "\t" if $opts{label_old} =~ / /; + $opts{label_new} .= "\t" if $opts{label_new} =~ / /; + } + push @options, '-L', $opts{label_old}, + '-L', $opts{label_new}; + } + # Generate diff + my $diffgen; + my $diff_pid = spawn( + exec => [ 'diff', '-u', @options, '--', $old, $new ], + env => { LC_ALL => 'C', LANG => 'C', TZ => 'UTC0' }, + to_pipe => \$diffgen, + ); + # Check diff and write it in patch file + my $difflinefound = 0; + my $binary = 0; + while (<$diffgen>) { + if (m/^(?:binary|[^-+\@ ].*\bdiffer\b)/i) { + $binary = 1; + &$handle_binary($self, $old, $new); + last; + } elsif (m/^[-+\@ ]/) { + $difflinefound++; + } elsif (m/^\\ /) { + warning(_g('file %s has no final newline (either ' . + 'original or modified version)'), $new); + } else { + chomp; + error(_g("unknown line from diff -u on %s: `%s'"), $new, $_); + } + if (*$self->{empty} and defined(*$self->{header})) { + $self->print(*$self->{header}) or syserr(_g('failed to write')); + *$self->{empty} = 0; + } + print { $self } $_ or syserr(_g('failed to write')); + } + close($diffgen) or syserr('close on diff pipe'); + wait_child($diff_pid, nocheck => 1, + cmdline => "diff -u @options -- $old $new"); + # Verify diff process ended successfully + # Exit code of diff: 0 => no difference, 1 => diff ok, 2 => error + # Ignore error if binary content detected + my $exit = WEXITSTATUS($?); + unless (WIFEXITED($?) && ($exit == 0 || $exit == 1 || $binary)) { + subprocerr(_g('diff on %s'), $new); + } + return ($exit == 0 || $exit == 1); +} + +sub add_diff_directory { + my ($self, $old, $new, %opts) = @_; + # TODO: make this function more configurable + # - offer to disable some checks + my $basedir = $opts{basedirname} || basename($new); + my $inc_removal = $opts{include_removal} || 0; + my $diff_ignore; + if ($opts{diff_ignore_func}) { + $diff_ignore = $opts{diff_ignore_func}; + } elsif ($opts{diff_ignore_regex}) { + $diff_ignore = sub { return $_[0] =~ /$opts{diff_ignore_regex}/o }; + } else { + $diff_ignore = sub { return 0 }; + } + + my @diff_files; + my %files_in_new; + my $scan_new = sub { + my $fn = (length > length($new)) ? substr($_, length($new) + 1) : '.'; + return if &$diff_ignore($fn); + $files_in_new{$fn} = 1; + lstat("$new/$fn") or syserr(_g('cannot stat file %s'), "$new/$fn"); + my $mode = S_IMODE((lstat(_))[2]); + my $size = (lstat(_))[7]; + if (-l _) { + unless (-l "$old/$fn") { + $self->_fail_not_same_type("$old/$fn", "$new/$fn"); + return; + } + my $n = readlink("$new/$fn"); + unless (defined $n) { + syserr(_g('cannot read link %s'), "$new/$fn"); + } + my $n2 = readlink("$old/$fn"); + unless (defined $n2) { + syserr(_g('cannot read link %s'), "$old/$fn"); + } + unless ($n eq $n2) { + $self->_fail_not_same_type("$old/$fn", "$new/$fn"); + } + } elsif (-f _) { + my $old_file = "$old/$fn"; + if (not lstat("$old/$fn")) { + if ($! != ENOENT) { + syserr(_g('cannot stat file %s'), "$old/$fn"); + } + $old_file = '/dev/null'; + } elsif (not -f _) { + $self->_fail_not_same_type("$old/$fn", "$new/$fn"); + return; + } + + my $label_old = "$basedir.orig/$fn"; + if ($opts{use_dev_null}) { + $label_old = $old_file if $old_file eq '/dev/null'; + } + push @diff_files, [$fn, $mode, $size, $old_file, "$new/$fn", + $label_old, "$basedir/$fn"]; + } elsif (-p _) { + unless (-p "$old/$fn") { + $self->_fail_not_same_type("$old/$fn", "$new/$fn"); + } + } elsif (-b _ || -c _ || -S _) { + $self->_fail_with_msg("$new/$fn", + _g('device or socket is not allowed')); + } elsif (-d _) { + if (not lstat("$old/$fn")) { + if ($! != ENOENT) { + syserr(_g('cannot stat file %s'), "$old/$fn"); + } + } elsif (not -d _) { + $self->_fail_not_same_type("$old/$fn", "$new/$fn"); + } + } else { + $self->_fail_with_msg("$new/$fn", _g('unknown file type')); + } + }; + my $scan_old = sub { + my $fn = (length > length($old)) ? substr($_, length($old) + 1) : '.'; + return if &$diff_ignore($fn); + return if $files_in_new{$fn}; + lstat("$old/$fn") or syserr(_g('cannot stat file %s'), "$old/$fn"); + if (-f _) { + if ($inc_removal) { + push @diff_files, [$fn, 0, 0, "$old/$fn", '/dev/null', + "$basedir.orig/$fn", '/dev/null']; + } else { + warning(_g('ignoring deletion of file %s'), $fn); + } + } elsif (-d _) { + warning(_g('ignoring deletion of directory %s'), $fn); + } elsif (-l _) { + warning(_g('ignoring deletion of symlink %s'), $fn); + } else { + $self->_fail_not_same_type("$old/$fn", "$new/$fn"); + } + }; + + find({ wanted => $scan_new, no_chdir => 1 }, $new); + find({ wanted => $scan_old, no_chdir => 1 }, $old); + + if ($opts{order_from} and -e $opts{order_from}) { + my $order_from = Dpkg::Source::Patch->new( + filename => $opts{order_from}); + my $analysis = $order_from->analyze($basedir, verbose => 0); + my %patchorder; + my $i = 0; + foreach my $fn (@{$analysis->{patchorder}}) { + $fn =~ s{^[^/]+/}{}; + $patchorder{$fn} = $i++; + } + # 'quilt refresh' sorts files as follows: + # - Any files in the existing patch come first, in the order in + # which they appear in the existing patch. + # - New files follow, sorted lexicographically. + # This seems a reasonable policy to follow, and avoids autopatches + # being shuffled when they are regenerated. + foreach my $diff_file (sort { $a->[0] cmp $b->[0] } @diff_files) { + my $fn = $diff_file->[0]; + $patchorder{$fn} = $i++ unless exists $patchorder{$fn}; + } + @diff_files = sort { $patchorder{$a->[0]} <=> $patchorder{$b->[0]} } + @diff_files; + } else { + @diff_files = sort { $a->[0] cmp $b->[0] } @diff_files; + } + + foreach my $diff_file (@diff_files) { + my ($fn, $mode, $size, + $old_file, $new_file, $label_old, $label_new) = @$diff_file; + my $success = $self->add_diff_file($old_file, $new_file, + label_old => $label_old, + label_new => $label_new, %opts); + if ($success and + $old_file eq '/dev/null' and $new_file ne '/dev/null') { + if (not $size) { + warning(_g("newly created empty file '%s' will not " . + 'be represented in diff'), $fn); + } else { + if ($mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + warning(_g("executable mode %04o of '%s' will " . + 'not be represented in diff'), $mode, $fn) + unless $fn eq 'debian/rules'; + } + if ($mode & (S_ISUID | S_ISGID | S_ISVTX)) { + warning(_g("special mode %04o of '%s' will not " . + 'be represented in diff'), $mode, $fn); + } + } + } + } +} + +sub finish { + my ($self) = @_; + close($self) or syserr(_g('cannot close %s'), $self->get_filename()); + return not *$self->{errors}; +} + +sub register_error { + my ($self) = @_; + *$self->{errors}++; +} +sub _fail_with_msg { + my ($self, $file, $msg) = @_; + errormsg(_g('cannot represent change to %s: %s'), $file, $msg); + $self->register_error(); +} +sub _fail_not_same_type { + my ($self, $old, $new) = @_; + my $old_type = get_type($old); + my $new_type = get_type($new); + errormsg(_g('cannot represent change to %s:'), $new); + errormsg(_g(' new version is %s'), $new_type); + errormsg(_g(' old version is %s'), $old_type); + $self->register_error(); +} + +sub _getline { + my $handle = shift; + + my $line = <$handle>; + if (defined $line) { + # Strip end-of-line chars + chomp($line); + $line =~ s/\r$//; + } + return $line; +} + +sub _intuit_file_patched { + my ($old, $new) = @_; + + return $new unless defined $old; + return $old unless defined $new; + return $new if -e $new and not -e $old; + return $old if -e $old and not -e $new; + + # We don't consider the case where both files are non-existent and + # where patch picks the one with the fewest directories to create + # since dpkg-source will pre-create the required directories + + # Precalculate metrics used by patch + my ($tmp_o, $tmp_n) = ($old, $new); + my ($len_o, $len_n) = (length($old), length($new)); + $tmp_o =~ s{[/\\]+}{/}g; + $tmp_n =~ s{[/\\]+}{/}g; + my $nb_comp_o = ($tmp_o =~ tr{/}{/}); + my $nb_comp_n = ($tmp_n =~ tr{/}{/}); + $tmp_o =~ s{^.*/}{}; + $tmp_n =~ s{^.*/}{}; + my ($blen_o, $blen_n) = (length($tmp_o), length($tmp_n)); + + # Decide like patch would + if ($nb_comp_o != $nb_comp_n) { + return ($nb_comp_o < $nb_comp_n) ? $old : $new; + } elsif ($blen_o != $blen_n) { + return ($blen_o < $blen_n) ? $old : $new; + } elsif ($len_o != $len_n) { + return ($len_o < $len_n) ? $old : $new; + } + return $old; +} + +# Fetch the header filename ignoring the optional timestamp +sub _fetch_filename { + my ($diff, $header) = @_; + + # Strip any leading spaces. + $header =~ s/^\s+//; + + # Is it a C-style string? + if ($header =~ m/^"/) { + error(_g('diff %s patches file with C-style encoded filename'), $diff); + } else { + # Tab is the official separator, it's always used when + # filename contain spaces. Try it first, otherwise strip on space + # if there's no tab + $header =~ s/\s.*// unless $header =~ s/\t.*//; + } + + return $header; +} + +# check diff for sanity, find directories to create as a side effect +sub analyze { + my ($self, $destdir, %opts) = @_; + + $opts{verbose} //= 1; + my $diff = $self->get_filename(); + my %filepatched; + my %dirtocreate; + my @patchorder; + my $patch_header = ''; + my $diff_count = 0; + + $_ = _getline($self); + + HUNK: + while (defined($_) or not eof($self)) { + my (%path, %fn); + # skip comments leading up to patch (if any) + while (1) { + if (/^(?:--- |\+\+\+ |@@ -)/) { + last; + } else { + $patch_header .= "$_\n"; + } + last HUNK if not defined($_ = _getline($self)); + } + $diff_count++; + # read file header (---/+++ pair) + unless(s/^--- //) { + error(_g("expected ^--- in line %d of diff `%s'"), $., $diff); + } + $path{old} = $_ = _fetch_filename($diff, $_); + $fn{old} = $_ if $_ ne '/dev/null' and s{^[^/]*/+}{$destdir/}; + if (/\.dpkg-orig$/) { + error(_g("diff `%s' patches file with name ending .dpkg-orig"), $diff); + } + + unless (defined($_ = _getline($self))) { + error(_g("diff `%s' finishes in middle of ---/+++ (line %d)"), $diff, $.); + } + unless (s/^\+\+\+ //) { + error(_g("line after --- isn't as expected in diff `%s' (line %d)"), $diff, $.); + } + $path{new} = $_ = _fetch_filename($diff, $_); + $fn{new} = $_ if $_ ne '/dev/null' and s{^[^/]*/+}{$destdir/}; + + unless (defined $fn{old} or defined $fn{new}) { + error(_g("none of the filenames in ---/+++ are valid in diff '%s' (line %d)"), + $diff, $.); + } + + # Safety checks on both filenames that patch could use + foreach my $key ('old', 'new') { + next unless defined $fn{$key}; + if ($path{$key} =~ m{/\.\./}) { + error(_g('%s contains an insecure path: %s'), $diff, $path{$key}); + } + my $path = $fn{$key}; + while (1) { + if (-l $path) { + error(_g('diff %s modifies file %s through a symlink: %s'), + $diff, $fn{$key}, $path); + } + last unless $path =~ s{/+[^/]*$}{}; + last if length($path) <= length($destdir); # $destdir is assumed safe + } + } + + if ($path{old} eq '/dev/null' and $path{new} eq '/dev/null') { + error(_g("original and modified files are /dev/null in diff `%s' (line %d)"), + $diff, $.); + } elsif ($path{new} eq '/dev/null') { + error(_g("file removal without proper filename in diff `%s' (line %d)"), + $diff, $. - 1) unless defined $fn{old}; + if ($opts{verbose}) { + warning(_g('diff %s removes a non-existing file %s (line %d)'), + $diff, $fn{old}, $.) unless -e $fn{old}; + } + } + my $fn = _intuit_file_patched($fn{old}, $fn{new}); + + my $dirname = $fn; + if ($dirname =~ s{/[^/]+$}{} and not -d $dirname) { + $dirtocreate{$dirname} = 1; + } + + if (-e $fn and not -f _) { + error(_g("diff `%s' patches something which is not a plain file"), $diff); + } + + if ($filepatched{$fn}) { + warning(_g("diff `%s' patches file %s twice"), $diff, $fn) + if $opts{verbose}; + } else { + $filepatched{$fn} = 1; + push @patchorder, $fn; + } + + # read hunks + my $hunk = 0; + while (defined($_ = _getline($self))) { + # read hunk header (@@) + next if /^\\ /; + last unless (/^@@ -\d+(,(\d+))? \+\d+(,(\d+))? @\@( .*)?$/); + my ($olines, $nlines) = ($1 ? $2 : 1, $3 ? $4 : 1); + # read hunk + while ($olines || $nlines) { + unless (defined($_ = _getline($self))) { + if (($olines == $nlines) and ($olines < 3)) { + warning(_g("unexpected end of diff `%s'"), $diff) + if $opts{verbose}; + last; + } else { + error(_g("unexpected end of diff `%s'"), $diff); + } + } + next if /^\\ /; + # Check stats + if (/^ / || /^$/) { --$olines; --$nlines; } + elsif (/^-/) { --$olines; } + elsif (/^\+/) { --$nlines; } + else { + error(_g("expected [ +-] at start of line %d of diff `%s'"), + $., $diff); + } + } + $hunk++; + } + unless($hunk) { + error(_g("expected ^\@\@ at line %d of diff `%s'"), $., $diff); + } + } + close($self); + unless ($diff_count) { + warning(_g("diff `%s' doesn't contain any patch"), $diff) + if $opts{verbose}; + } + *$self->{analysis}{$destdir}{dirtocreate} = \%dirtocreate; + *$self->{analysis}{$destdir}{filepatched} = \%filepatched; + *$self->{analysis}{$destdir}{patchorder} = \@patchorder; + *$self->{analysis}{$destdir}{patchheader} = $patch_header; + return *$self->{analysis}{$destdir}; +} + +sub prepare_apply { + my ($self, $analysis, %opts) = @_; + if ($opts{create_dirs}) { + foreach my $dir (keys %{$analysis->{dirtocreate}}) { + eval { mkpath($dir, 0, 0777); }; + syserr(_g('cannot create directory %s'), $dir) if $@; + } + } +} + +sub apply { + my ($self, $destdir, %opts) = @_; + # Set default values to options + $opts{force_timestamp} = 1 unless exists $opts{force_timestamp}; + $opts{remove_backup} = 1 unless exists $opts{remove_backup}; + $opts{create_dirs} = 1 unless exists $opts{create_dirs}; + $opts{options} ||= [ '-t', '-F', '0', '-N', '-p1', '-u', + '-V', 'never', '-g0', '-b', '-z', '.dpkg-orig']; + $opts{add_options} ||= []; + push @{$opts{options}}, @{$opts{add_options}}; + # Check the diff and create missing directories + my $analysis = $self->analyze($destdir, %opts); + $self->prepare_apply($analysis, %opts); + # Apply the patch + $self->ensure_open('r'); + my ($stdout, $stderr) = ('', ''); + spawn( + exec => [ 'patch', @{$opts{options}} ], + chdir => $destdir, + env => { LC_ALL => 'C', LANG => 'C' }, + delete_env => [ 'POSIXLY_CORRECT' ], # ensure expected patch behaviour + wait_child => 1, + nocheck => 1, + from_handle => $self->get_filehandle(), + to_string => \$stdout, + error_to_string => \$stderr, + ); + if ($?) { + print { *STDOUT } $stdout; + print { *STDERR } $stderr; + subprocerr('LC_ALL=C patch ' . join(' ', @{$opts{options}}) . + ' < ' . $self->get_filename()); + } + $self->close(); + # Reset the timestamp of all the patched files + # and remove .dpkg-orig files + my @files = keys %{$analysis->{filepatched}}; + my $now = $opts{timestamp}; + $now ||= fs_time($files[0]) if $opts{force_timestamp} && scalar @files; + foreach my $fn (@files) { + if ($opts{force_timestamp}) { + utime($now, $now, $fn) or $! == ENOENT + or syserr(_g('cannot change timestamp for %s'), $fn); + } + if ($opts{remove_backup}) { + $fn .= '.dpkg-orig'; + unlink($fn) or syserr(_g('remove patch backup file %s'), $fn); + } + } + return $analysis; +} + +# Verify if check will work... +sub check_apply { + my ($self, $destdir, %opts) = @_; + # Set default values to options + $opts{create_dirs} = 1 unless exists $opts{create_dirs}; + $opts{options} ||= [ '--dry-run', '-s', '-t', '-F', '0', '-N', '-p1', '-u', + '-V', 'never', '-g0', '-b', '-z', '.dpkg-orig']; + $opts{add_options} ||= []; + push @{$opts{options}}, @{$opts{add_options}}; + # Check the diff and create missing directories + my $analysis = $self->analyze($destdir, %opts); + $self->prepare_apply($analysis, %opts); + # Apply the patch + $self->ensure_open('r'); + my $patch_pid = spawn( + exec => [ 'patch', @{$opts{options}} ], + chdir => $destdir, + env => { LC_ALL => 'C', LANG => 'C' }, + delete_env => [ 'POSIXLY_CORRECT' ], # ensure expected patch behaviour + from_handle => $self->get_filehandle(), + to_file => '/dev/null', + error_to_file => '/dev/null', + ); + wait_child($patch_pid, nocheck => 1); + my $exit = WEXITSTATUS($?); + subprocerr('patch --dry-run') unless WIFEXITED($?); + $self->close(); + return ($exit == 0); +} + +# Helper functions +sub get_type { + my $file = shift; + if (not lstat($file)) { + return _g('nonexistent') if $! == ENOENT; + syserr(_g('cannot stat %s'), $file); + } else { + -f _ && return _g('plain file'); + -d _ && return _g('directory'); + -l _ && return sprintf(_g('symlink to %s'), readlink($file)); + -b _ && return _g('block device'); + -c _ && return _g('character device'); + -p _ && return _g('named pipe'); + -S _ && return _g('named socket'); + } +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Source/Quilt.pm b/third_party/dpkg-dev/scripts/Dpkg/Source/Quilt.pm new file mode 100644 index 0000000..7716927 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Source/Quilt.pm
@@ -0,0 +1,308 @@ +# Copyright © 2008-2012 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Source::Quilt; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Source::Patch; +use Dpkg::Source::Functions qw(erasedir fs_time); +use Dpkg::Vendor qw(get_current_vendor); + +use File::Spec; +use File::Copy; +use File::Find; +use File::Path qw(make_path); +use File::Basename; + +sub new { + my ($this, $dir, %opts) = @_; + my $class = ref($this) || $this; + + my $self = { + dir => $dir, + }; + bless $self, $class; + + $self->load_series(); + $self->load_db(); + + return $self; +} + +sub setup_db { + my ($self) = @_; + my $db_dir = $self->get_db_file(); + if (not -d $db_dir) { + mkdir $db_dir or syserr(_g('cannot mkdir %s'), $db_dir); + } + my $file = $self->get_db_file('.version'); + if (not -e $file) { + open(my $version_fh, '>', $file) or syserr(_g('cannot write %s'), $file); + print { $version_fh } "2\n"; + close($version_fh); + } + # The files below are used by quilt to know where patches are stored + # and what file contains the patch list (supported by quilt >= 0.48-5 + # in Debian). + $file = $self->get_db_file('.quilt_patches'); + if (not -e $file) { + open(my $qpatch_fh, '>', $file) or syserr(_g('cannot write %s'), $file); + print { $qpatch_fh } "debian/patches\n"; + close($qpatch_fh); + } + $file = $self->get_db_file('.quilt_series'); + if (not -e $file) { + open(my $qseries_fh, '>', $file) or syserr(_g('cannot write %s'), $file); + my $series = $self->get_series_file(); + $series = (File::Spec->splitpath($series))[2]; + print { $qseries_fh } "$series\n"; + close($qseries_fh); + } +} + +sub load_db { + my ($self) = @_; + + my $pc_applied = $self->get_db_file('applied-patches'); + $self->{applied_patches} = [ $self->read_patch_list($pc_applied) ]; +} + +sub write_db { + my ($self) = @_; + + $self->setup_db(); + my $pc_applied = $self->get_db_file('applied-patches'); + open(my $applied_fh, '>', $pc_applied) or + syserr(_g('cannot write %s'), $pc_applied); + foreach my $patch (@{$self->{applied_patches}}) { + print { $applied_fh } "$patch\n"; + } + close($applied_fh); +} + +sub load_series { + my ($self, %opts) = @_; + + my $series = $self->get_series_file(); + $self->{series} = [ $self->read_patch_list($series, %opts) ]; +} + +sub series { + my ($self) = @_; + return @{$self->{series}}; +} + +sub applied { + my ($self) = @_; + return @{$self->{applied_patches}}; +} + +sub top { + my ($self) = @_; + my $count = scalar @{$self->{applied_patches}}; + return $self->{applied_patches}[$count - 1] if $count; + return; +} + +sub next { + my ($self) = @_; + my $count_applied = scalar @{$self->{applied_patches}}; + my $count_series = scalar @{$self->{series}}; + return $self->{series}[$count_applied] if ($count_series > $count_applied); + return; +} + +sub push { + my ($self, %opts) = @_; + $opts{verbose} //= 0; + $opts{timestamp} //= fs_time($self->{dir}); + + my $patch = $self->next(); + return unless defined $patch; + + my $path = $self->get_patch_file($patch); + my $obj = Dpkg::Source::Patch->new(filename => $path); + + info(_g('applying %s'), $patch) if $opts{verbose}; + eval { + $obj->apply($self->{dir}, timestamp => $opts{timestamp}, + verbose => $opts{verbose}, + force_timestamp => 1, create_dirs => 1, remove_backup => 0, + options => [ '-t', '-F', '0', '-N', '-p1', '-u', + '-V', 'never', '-g0', '-E', '-b', + '-B', ".pc/$patch/", '--reject-file=-' ]); + }; + if ($@) { + info(_g('fuzz is not allowed when applying patches')); + info(_g("if patch '%s' is correctly applied by quilt, use '%s' to update it"), + $patch, 'quilt refresh'); + $self->restore_quilt_backup_files($patch, %opts); + erasedir($self->get_db_file($patch)); + die $@; + } + CORE::push @{$self->{applied_patches}}, $patch; + $self->write_db(); +} + +sub pop { + my ($self, %opts) = @_; + $opts{verbose} //= 0; + $opts{timestamp} //= fs_time($self->{dir}); + $opts{reverse_apply} //= 0; + + my $patch = $self->top(); + return unless defined $patch; + + info(_g('unapplying %s'), $patch) if $opts{verbose}; + my $backup_dir = $self->get_db_file($patch); + if (-d $backup_dir and not $opts{reverse_apply}) { + # Use the backup copies to restore + $self->restore_quilt_backup_files($patch); + } else { + # Otherwise reverse-apply the patch + my $path = $self->get_patch_file($patch); + my $obj = Dpkg::Source::Patch->new(filename => $path); + + $obj->apply($self->{dir}, timestamp => $opts{timestamp}, + verbose => 0, force_timestamp => 1, remove_backup => 0, + options => [ '-R', '-t', '-N', '-p1', + '-u', '-V', 'never', '-g0', '-E', + '--no-backup-if-mismatch' ]); + } + + erasedir($backup_dir); + pop @{$self->{applied_patches}}; + $self->write_db(); +} + +sub get_db_version { + my ($self) = @_; + my $pc_ver = $self->get_db_file('.version'); + if (-f $pc_ver) { + open(my $ver_fh, '<', $pc_ver) or syserr(_g('cannot read %s'), $pc_ver); + my $version = <$ver_fh>; + chomp $version; + close($ver_fh); + return $version; + } + return; +} + +sub find_problems { + my ($self) = @_; + my $patch_dir = $self->get_patch_file(); + if (-e $patch_dir and not -d _) { + return sprintf(_g('%s should be a directory or non-existing'), $patch_dir); + } + my $series = $self->get_series_file(); + if (-e $series and not -f _) { + return sprintf(_g('%s should be a file or non-existing'), $series); + } + return; +} + +sub get_series_file { + my ($self) = @_; + my $vendor = lc(get_current_vendor() || 'debian'); + # Series files are stored alongside patches + my $default_series = $self->get_patch_file('series'); + my $vendor_series = $self->get_patch_file("$vendor.series"); + return $vendor_series if -e $vendor_series; + return $default_series; +} + +sub get_db_file { + my $self = shift; + return File::Spec->catfile($self->{dir}, '.pc', @_); +} + +sub get_db_dir { + my ($self) = @_; + return $self->get_db_file(); +} + +sub get_patch_file { + my $self = shift; + return File::Spec->catfile($self->{dir}, 'debian', 'patches', @_); +} + +sub get_patch_dir { + my ($self) = @_; + return $self->get_patch_file(); +} + +## METHODS BELOW ARE INTERNAL ## + +sub read_patch_list { + my ($self, $file, %opts) = @_; + return () if not defined $file or not -f $file; + $opts{warn_options} //= 0; + my @patches; + open(my $series_fh, '<' , $file) or syserr(_g('cannot read %s'), $file); + while (defined($_ = <$series_fh>)) { + chomp; s/^\s+//; s/\s+$//; # Strip leading/trailing spaces + s/(^|\s+)#.*$//; # Strip comment + next unless $_; + if (/^(\S+)\s+(.*)$/) { + $_ = $1; + if ($2 ne '-p1') { + warning(_g('the series file (%s) contains unsupported ' . + "options ('%s', line %s); dpkg-source might " . + 'fail when applying patches'), + $file, $2, $.) if $opts{warn_options}; + } + } + error(_g('%s contains an insecure path: %s'), $file, $_) if m{(^|/)\.\./}; + CORE::push @patches, $_; + } + close($series_fh); + return @patches; +} + +sub restore_quilt_backup_files { + my ($self, $patch, %opts) = @_; + my $patch_dir = $self->get_db_file($patch); + return unless -d $patch_dir; + info(_g('restoring quilt backup files for %s'), $patch) if $opts{verbose}; + find({ + no_chdir => 1, + wanted => sub { + return if -d $_; + my $relpath_in_srcpkg = File::Spec->abs2rel($_, $patch_dir); + my $target = File::Spec->catfile($self->{dir}, $relpath_in_srcpkg); + if (-s $_) { + unlink($target); + make_path(dirname($target)); + unless (link($_, $target)) { + copy($_, $target) + or syserr(_g('failed to copy %s to %s'), $_, $target); + chmod((stat(_))[2], $target) + or syserr(_g("unable to change permission of `%s'"), $target); + } + } else { + # empty files are "backups" for new files that patch created + unlink($target); + } + } + }, $patch_dir); +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Substvars.pm b/third_party/dpkg-dev/scripts/Dpkg/Substvars.pm new file mode 100644 index 0000000..e287dc9f --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Substvars.pm
@@ -0,0 +1,337 @@ +# Copyright © 2006-2009,2012 Guillem Jover <guillem@debian.org> +# Copyright © 2007-2010 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Substvars; + +use strict; +use warnings; + +our $VERSION = '1.02'; + +use Dpkg (); +use Dpkg::Arch qw(get_host_arch); +use Dpkg::ErrorHandling; +use Dpkg::Gettext; + +use Carp; +use POSIX qw(:errno_h); + +use parent qw(Dpkg::Interface::Storable); + +my $maxsubsts = 50; + +=encoding utf8 + +=head1 NAME + +Dpkg::Substvars - handle variable substitution in strings + +=head1 DESCRIPTION + +It provides some an object which is able to substitute variables in +strings. + +=head1 METHODS + +=over 8 + +=item my $s = Dpkg::Substvars->new($file) + +Create a new object that can do substitutions. By default it contains +generic substitutions like ${Newline}, ${Space}, ${Tab}, ${dpkg:Version} +and ${dpkg:Upstream-Version}. + +Additional substitutions will be read from the $file passed as parameter. + +It keeps track of which substitutions were actually used (only counting +substvars(), not get()), and warns about unused substvars when asked to. The +substitutions that are always present are not included in these warnings. + +=cut + +sub new { + my ($this, $arg) = @_; + my $class = ref($this) || $this; + my $self = { + vars => { + 'Newline' => "\n", + 'Space' => ' ', + 'Tab' => "\t", + 'dpkg:Version' => $Dpkg::PROGVERSION, + 'dpkg:Upstream-Version' => $Dpkg::PROGVERSION, + }, + used => {}, + msg_prefix => '', + }; + $self->{vars}{'dpkg:Upstream-Version'} =~ s/-[^-]+$//; + bless $self, $class; + $self->mark_as_used($_) foreach keys %{$self->{vars}}; + if ($arg) { + $self->load($arg) if -e $arg; + } + return $self; +} + +=item $s->set($key, $value) + +Add/replace a substitution. + +=cut + +sub set { + my ($self, $key, $value) = @_; + $self->{vars}{$key} = $value; +} + +=item $s->set_as_used($key, $value) + +Add/replace a substitution and mark it as used (no warnings will be produced +even if unused). + +=cut + +sub set_as_used { + my ($self, $key, $value) = @_; + $self->set($key, $value); + $self->mark_as_used($key); +} + +=item $s->get($key) + +Get the value of a given substitution. + +=cut + +sub get { + my ($self, $key) = @_; + return $self->{vars}{$key}; +} + +=item $s->delete($key) + +Remove a given substitution. + +=cut + +sub delete { + my ($self, $key) = @_; + delete $self->{used}{$key}; + return delete $self->{vars}{$key}; +} + +=item $s->mark_as_used($key) + +Prevents warnings about a unused substitution, for example if it is provided by +default. + +=cut + +sub mark_as_used { + my ($self, $key) = @_; + $self->{used}{$key}++; +} + +=item $s->no_warn($key) + +Obsolete function, use mark_as_used() instead. + +=cut + +sub no_warn { + my ($self, $key) = @_; + carp 'obsolete no_warn() function, use mark_as_used() instead'; + $self->mark_as_used($key); +} + +=item $s->load($file) + +Add new substitutions read from $file. + +=item $s->parse($fh, $desc) + +Add new substitutions read from the filehandle. $desc is used to identify +the filehandle in error messages. + +=cut + +sub parse { + my ($self, $fh, $varlistfile) = @_; + binmode($fh); + while (<$fh>) { + next if m/^\s*\#/ || !m/\S/; + s/\s*\n$//; + if (! m/^(\w[-:0-9A-Za-z]*)\=(.*)$/) { + error(_g('bad line in substvars file %s at line %d'), + $varlistfile, $.); + } + $self->{vars}{$1} = $2; + } +} + +=item $s->set_version_substvars($sourceversion, $binaryversion) + +Defines ${binary:Version}, ${source:Version} and +${source:Upstream-Version} based on the given version strings. + +These will never be warned about when unused. + +=cut + +sub set_version_substvars { + my ($self, $sourceversion, $binaryversion) = @_; + + # Handle old function signature taking only one argument. + $binaryversion ||= $sourceversion; + + # For backwards compatibility on binNMUs that do not use the Binary-Only + # field on the changelog, always fix up the source version. + $sourceversion =~ s/\+b[0-9]+$//; + + $self->{vars}{'binary:Version'} = $binaryversion; + $self->{vars}{'source:Version'} = $sourceversion; + $self->{vars}{'source:Upstream-Version'} = $sourceversion; + $self->{vars}{'source:Upstream-Version'} =~ s/-[^-]*$//; + + # XXX: Source-Version is now deprecated, remove in the future. + $self->{vars}{'Source-Version'} = $binaryversion; + + $self->mark_as_used($_) foreach qw/binary:Version source:Version source:Upstream-Version Source-Version/; +} + +=item $s->set_arch_substvars() + +Defines architecture variables: ${Arch}. + +This will never be warned about when unused. + +=cut + +sub set_arch_substvars { + my ($self) = @_; + + $self->set_as_used('Arch', get_host_arch()); +} + +=item $newstring = $s->substvars($string) + +Substitutes variables in $string and return the result in $newstring. + +=cut + +sub substvars { + my ($self, $v, %opts) = @_; + my $lhs; + my $vn; + my $rhs = ''; + my $count = 0; + $opts{msg_prefix} = $self->{msg_prefix} unless exists $opts{msg_prefix}; + $opts{no_warn} = 0 unless exists $opts{no_warn}; + + while ($v =~ m/^(.*?)\$\{([-:0-9a-z]+)\}(.*)$/si) { + # If we have consumed more from the leftover data, then + # reset the recursive counter. + $count = 0 if (length($3) < length($rhs)); + + if ($count >= $maxsubsts) { + error($opts{msg_prefix} . + _g("too many substitutions - recursive ? - in \`%s'"), $v); + } + $lhs = $1; $vn = $2; $rhs = $3; + if (defined($self->{vars}{$vn})) { + $v = $lhs . $self->{vars}{$vn} . $rhs; + $self->mark_as_used($vn); + $count++; + } else { + warning($opts{msg_prefix} . _g('unknown substitution variable ${%s}'), + $vn) unless $opts{no_warn}; + $v = $lhs . $rhs; + } + } + return $v; +} + +=item $s->warn_about_unused() + +Issues warning about any variables that were set, but not used + +=cut + +sub warn_about_unused { + my ($self, %opts) = @_; + $opts{msg_prefix} = $self->{msg_prefix} unless exists $opts{msg_prefix}; + + foreach my $vn (keys %{$self->{vars}}) { + next if $self->{used}{$vn}; + # Empty substitutions variables are ignored on the basis + # that they are not required in the current situation + # (example: debhelper's misc:Depends in many cases) + next if $self->{vars}{$vn} eq ''; + warning($opts{msg_prefix} . _g('unused substitution variable ${%s}'), + $vn); + } +} + +=item $s->set_msg_prefix($prefix) + +Define a prefix displayed before all warnings/error messages output +by the module. + +=cut + +sub set_msg_prefix { + my ($self, $prefix) = @_; + $self->{msg_prefix} = $prefix; +} + +=item $s->save($file) + +Store all substitutions variables except the automatic ones in the +indicated file. + +=item "$s" + +Return a string representation of all substitutions variables except the +automatic ones. + +=item $str = $s->output($fh) + +Print all substitutions variables except the automatic ones in the +filehandle and return the content written. + +=cut + +sub output { + my ($self, $fh) = @_; + my $str = ''; + # Store all non-automatic substitutions only + foreach my $vn (sort keys %{$self->{vars}}) { + next if /^(?:(?:dpkg|source|binary):(?:Source-)?Version|Space|Tab|Newline|Arch|Source-Version|F:.+)$/; + my $line = "$vn=" . $self->{vars}{$vn} . "\n"; + print { $fh } $line if defined $fh; + $str .= $line; + } + return $str; +} + +=back + +=head1 AUTHOR + +Raphaël Hertzog <hertzog@debian.org>. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Util.pm b/third_party/dpkg-dev/scripts/Dpkg/Util.pm new file mode 100644 index 0000000..504abd5e --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Util.pm
@@ -0,0 +1,51 @@ +# Copyright © 2013 Guillem Jover <guillem@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Util; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use Exporter qw(import); +our @EXPORT_OK = qw(any none); +our %EXPORT_TAGS = (list => [ qw(any none) ]); + +# XXX: Ideally we would use List::MoreUtils, but that's not a core module, +# so to avoid the additional dependency we'll make do with the following +# trivial reimplementations. + +sub any(&@) { + my $code = shift; + + foreach (@_) { + return 1 if $code->(); + } + + return 0; +} + +sub none(&@) { + my $code = shift; + + foreach (@_) { + return 0 if $code->(); + } + + return 1; +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Vars.pm b/third_party/dpkg-dev/scripts/Dpkg/Vars.pm new file mode 100644 index 0000000..0186bcd --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Vars.pm
@@ -0,0 +1,50 @@ +# Copyright © 2007-2009,2012-2013 Guillem Jover <guillem@debian.org> +# Copyright © 2007 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Vars; + +use strict; +use warnings; + +our $VERSION = '0.03'; + +use Dpkg::ErrorHandling; +use Dpkg::Gettext; +use Dpkg::Package; + +use Exporter qw(import); +our @EXPORT = qw(get_source_package set_source_package); + +my $sourcepackage; + +sub get_source_package { + return $sourcepackage; +} + +sub set_source_package { + my $v = shift; + my $err = pkg_name_is_illegal($v); + error(_g("source package name '%s' is illegal: %s"), $v, $err) if $err; + + if (not defined($sourcepackage)) { + $sourcepackage = $v; + } elsif ($v ne $sourcepackage) { + error(_g('source package has two conflicting values - %s and %s'), + $sourcepackage, $v); + } +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Vendor.pm b/third_party/dpkg-dev/scripts/Dpkg/Vendor.pm new file mode 100644 index 0000000..96d0976 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Vendor.pm
@@ -0,0 +1,182 @@ +# Copyright © 2008-2009 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Vendor; + +use strict; +use warnings; + +our $VERSION = '1.01'; + +use Dpkg (); +use Dpkg::ErrorHandling; +use Dpkg::Gettext; +use Dpkg::BuildEnv; +use Dpkg::Control::HashCore; + +use Exporter qw(import); +our @EXPORT_OK = qw(get_vendor_info get_current_vendor get_vendor_file + get_vendor_dir get_vendor_object run_vendor_hook); + +my $origins = "$Dpkg::CONFDIR/origins"; +$origins = $ENV{DPKG_ORIGINS_DIR} if $ENV{DPKG_ORIGINS_DIR}; + +=encoding utf8 + +=head1 NAME + +Dpkg::Vendor - get access to some vendor specific information + +=head1 DESCRIPTION + +The files in $Dpkg::CONFDIR/origins/ can provide information about various +vendors who are providing Debian packages. Currently those files look like +this: + + Vendor: Debian + Vendor-URL: http://www.debian.org/ + Bugs: debbugs://bugs.debian.org + +If the vendor derives from another vendor, the file should document +the relationship by listing the base distribution in the Parent field: + + Parent: Debian + +The file should be named according to the vendor name. + +=head1 FUNCTIONS + +=over 4 + +=item $dir = Dpkg::Vendor::get_vendor_dir() + +Returns the current dpkg origins directory name, where the vendor files +are stored. + +=cut + +sub get_vendor_dir { + return $origins; +} + +=item $fields = Dpkg::Vendor::get_vendor_info($name) + +Returns a Dpkg::Control object with the information parsed from the +corresponding vendor file in $Dpkg::CONFDIR/origins/. If $name is omitted, +it will use $Dpkg::CONFDIR/origins/default which is supposed to be a symlink +to the vendor of the currently installed operating system. Returns undef +if there's no file for the given vendor. + +=cut + +sub get_vendor_info(;$) { + my $vendor = shift || 'default'; + my $file = get_vendor_file($vendor); + return unless $file; + my $fields = Dpkg::Control::HashCore->new(); + $fields->load($file) or error(_g('%s is empty'), $file); + return $fields; +} + +=item $name = Dpkg::Vendor::get_vendor_file($name) + +Check if there's a file for the given vendor and returns its +name. + +=cut + +sub get_vendor_file(;$) { + my $vendor = shift || 'default'; + my $file; + my @tries = ($vendor, lc($vendor), ucfirst($vendor), ucfirst(lc($vendor))); + if ($vendor =~ s/\s+/-/) { + push @tries, $vendor, lc($vendor), ucfirst($vendor), ucfirst(lc($vendor)); + } + foreach my $name (@tries) { + $file = "$origins/$name" if -e "$origins/$name"; + } + return $file; +} + +=item $name = Dpkg::Vendor::get_current_vendor() + +Returns the name of the current vendor. If DEB_VENDOR is set, it uses +that first, otherwise it falls back to parsing $Dpkg::CONFDIR/origins/default. +If that file doesn't exist, it returns undef. + +=cut + +sub get_current_vendor() { + my $f; + if (Dpkg::BuildEnv::has('DEB_VENDOR')) { + $f = get_vendor_info(Dpkg::BuildEnv::get('DEB_VENDOR')); + return $f->{'Vendor'} if defined $f; + } + $f = get_vendor_info(); + return $f->{'Vendor'} if defined $f; + return; +} + +=item $object = Dpkg::Vendor::get_vendor_object($name) + +Return the Dpkg::Vendor::* object of the corresponding vendor. +If $name is omitted, return the object of the current vendor. +If no vendor can be identified, then return the Dpkg::Vendor::Default +object. + +=cut + +my %OBJECT_CACHE; +sub get_vendor_object { + my $vendor = shift || get_current_vendor() || 'Default'; + return $OBJECT_CACHE{$vendor} if exists $OBJECT_CACHE{$vendor}; + + my ($obj, @names); + if ($vendor ne 'Default') { + push @names, $vendor, lc($vendor), ucfirst($vendor), ucfirst(lc($vendor)); + } + foreach my $name (@names, 'Default') { + eval qq{ + require Dpkg::Vendor::$name; + \$obj = Dpkg::Vendor::$name->new(); + }; + last unless $@; + } + $OBJECT_CACHE{$vendor} = $obj; + return $obj; +} + +=item Dpkg::Vendor::run_vendor_hook($hookid, @params) + +Run a hook implemented by the current vendor object. + +=cut + +sub run_vendor_hook { + my $vendor_obj = get_vendor_object(); + $vendor_obj->run_hook(@_); +} + +=back + +=head1 CHANGES + +=head2 Version 1.01 + +New function: get_vendor_dir(). + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Vendor/Debian.pm b/third_party/dpkg-dev/scripts/Dpkg/Vendor/Debian.pm new file mode 100644 index 0000000..9cce36d5 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Vendor/Debian.pm
@@ -0,0 +1,194 @@ +# Copyright © 2009-2011 Raphaël Hertzog <hertzog@debian.org> +# +# Hardening build flags handling derived from work of: +# Copyright © 2009-2011 Kees Cook <kees@debian.org> +# Copyright © 2007-2008 Canonical, Ltd. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Vendor::Debian; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use parent qw(Dpkg::Vendor::Default); + +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Control::Types; +use Dpkg::BuildOptions; +use Dpkg::Arch qw(get_host_arch debarch_to_debtriplet); + +=encoding utf8 + +=head1 NAME + +Dpkg::Vendor::Debian - Debian vendor object + +=head1 DESCRIPTION + +This vendor object customize the behaviour of dpkg scripts +for Debian specific actions. + +=cut + +sub run_hook { + my ($self, $hook, @params) = @_; + + if ($hook eq 'keyrings') { + return ('/usr/share/keyrings/debian-keyring.gpg', + '/usr/share/keyrings/debian-maintainers.gpg'); + } elsif ($hook eq 'register-custom-fields') { + } elsif ($hook eq 'extend-patch-header') { + my ($textref, $ch_info) = @params; + if ($ch_info->{'Closes'}) { + foreach my $bug (split(/\s+/, $ch_info->{'Closes'})) { + $$textref .= "Bug-Debian: http://bugs.debian.org/$bug\n"; + } + } + + # XXX: Layer violation... + require Dpkg::Vendor::Ubuntu; + my $b = Dpkg::Vendor::Ubuntu::find_launchpad_closes($ch_info->{'Changes'}); + foreach my $bug (@$b) { + $$textref .= "Bug-Ubuntu: https://bugs.launchpad.net/bugs/$bug\n"; + } + } elsif ($hook eq 'update-buildflags') { + $self->add_hardening_flags(@params); + } else { + return $self->SUPER::run_hook($hook, @params); + } +} + +sub add_hardening_flags { + my ($self, $flags) = @_; + my $arch = get_host_arch(); + my ($abi, $os, $cpu) = debarch_to_debtriplet($arch); + + unless (defined $abi and defined $os and defined $cpu) { + warning(_g("unknown host architecture '%s'"), $arch); + ($abi, $os, $cpu) = ('', '', ''); + } + + # Features enabled by default for all builds. + my %use_feature = ( + pie => 0, + stackprotector => 1, + fortify => 1, + format => 1, + relro => 1, + bindnow => 0, + ); + + # Adjust features based on Maintainer's desires. + my $opts = Dpkg::BuildOptions->new(envvar => 'DEB_BUILD_MAINT_OPTIONS'); + foreach my $feature (split(/,/, $opts->get('hardening') // '')) { + $feature = lc($feature); + if ($feature =~ s/^([+-])//) { + my $value = ($1 eq '+') ? 1 : 0; + if ($feature eq 'all') { + $use_feature{$_} = $value foreach keys %use_feature; + } else { + if (exists $use_feature{$feature}) { + $use_feature{$feature} = $value; + } else { + warning(_g('unknown hardening feature: %s'), $feature); + } + } + } else { + warning(_g('incorrect value in hardening option of ' . + 'DEB_BUILD_MAINT_OPTIONS: %s'), $feature); + } + } + + # Mask features that are not available on certain architectures. + if ($os !~ /^(linux|knetbsd|hurd)$/ or + $cpu =~ /^(hppa|mips|mipsel|avr32)$/) { + # Disabled on non-linux/knetbsd/hurd (see #430455 and #586215). + # Disabled on hppa, mips/mipsel (#532821), avr32 + # (#574716). + $use_feature{pie} = 0; + } + if ($cpu =~ /^(ia64|alpha|mips|mipsel|hppa)$/ or $arch eq 'arm') { + # Stack protector disabled on ia64, alpha, mips, mipsel, hppa. + # "warning: -fstack-protector not supported for this target" + # Stack protector disabled on arm (ok on armel). + # compiler supports it incorrectly (leads to SEGV) + $use_feature{stackprotector} = 0; + } + if ($cpu =~ /^(ia64|hppa|avr32)$/) { + # relro not implemented on ia64, hppa, avr32. + $use_feature{relro} = 0; + } + + # Mask features that might be influenced by other flags. + if ($flags->{build_options}->has('noopt')) { + # glibc 2.16 and later warn when using -O0 and _FORTIFY_SOURCE. + $use_feature{fortify} = 0; + } + + # Handle logical feature interactions. + if ($use_feature{relro} == 0) { + # Disable bindnow if relro is not enabled, since it has no + # hardening ability without relro and may incur load penalties. + $use_feature{bindnow} = 0; + } + + # PIE + if ($use_feature{pie}) { + $flags->append('CFLAGS', '-fPIE'); + $flags->append('FFLAGS', '-fPIE'); + $flags->append('CXXFLAGS', '-fPIE'); + $flags->append('GCJFLAGS', '-fPIE'); + $flags->append('LDFLAGS', '-fPIE -pie'); + } + + # Stack protector + if ($use_feature{stackprotector}) { + $flags->append('CFLAGS', '-fstack-protector --param=ssp-buffer-size=4'); + $flags->append('FFLAGS', '-fstack-protector --param=ssp-buffer-size=4'); + $flags->append('CXXFLAGS', '-fstack-protector --param=ssp-buffer-size=4'); + $flags->append('GCJFLAGS', '-fstack-protector --param=ssp-buffer-size=4'); + } + + # Fortify Source + if ($use_feature{fortify}) { + $flags->append('CPPFLAGS', '-D_FORTIFY_SOURCE=2'); + } + + # Format Security + if ($use_feature{format}) { + $flags->append('CFLAGS', '-Wformat -Werror=format-security'); + $flags->append('CXXFLAGS', '-Wformat -Werror=format-security'); + } + + # Read-only Relocations + if ($use_feature{relro}) { + $flags->append('LDFLAGS', '-Wl,-z,relro'); + } + + # Bindnow + if ($use_feature{bindnow}) { + $flags->append('LDFLAGS', '-Wl,-z,now'); + } + + # Store the feature usage. + while (my ($feature, $enabled) = each %use_feature) { + $flags->set_feature('hardening', $feature, $enabled); + } +} + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Vendor/Default.pm b/third_party/dpkg-dev/scripts/Dpkg/Vendor/Default.pm new file mode 100644 index 0000000..f2d8aff --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Vendor/Default.pm
@@ -0,0 +1,136 @@ +# Copyright © 2009 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Vendor::Default; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +# If you use this file as template to create a new vendor object, please +# uncomment the following lines +#use parent qw(Dpkg::Vendor::Default); + +=encoding utf8 + +=head1 NAME + +Dpkg::Vendor::Default - default vendor object + +=head1 DESCRIPTION + +A vendor object is used to provide vendor specific behaviour +in various places. This is the default object used in case +there's none for the current vendor or in case the vendor could +not be identified (see Dpkg::Vendor documentation). + +It provides some hooks that are called by various dpkg-* tools. +If you need a new hook, please file a bug against dpkg-dev and explain +your need. Note that the hook API has no guaranty to be stable over an +extended period. If you run an important distribution that makes use +of vendor hooks, you'd better submit them for integration so that +we avoid breaking your code. + +=head1 FUNCTIONS + +=over 4 + +=item $vendor_obj = Dpkg::Vendor::Default->new() + +Creates the default vendor object. Can be inherited by all vendor objects +if they don't need any specific initialization at object creation time. + +=cut + +sub new { + my ($this) = @_; + my $class = ref($this) || $this; + my $self = {}; + bless $self, $class; + return $self; +} + +=item $vendor_obj->run_hook($id, @params) + +Run the corresponding hook. The parameters are hook-specific. The +supported hooks are: + +=over 8 + +=item before-source-build ($srcpkg) + +The first parameter is a Dpkg::Source::Package object. The hook is called +just before the execution of $srcpkg->build(). + +=item keyrings () + +The hook is called when dpkg-source is checking a signature on a source +package. It takes no parameters, but returns a (possibly empty) list of +vendor-specific keyrings. + +=item register-custom-fields () + +The hook is called in Dpkg::Control::Fields to register custom fields. +You should return a list of arrays. Each array is an operation to perform. +The first item is the name of the operation and corresponds +to a field_* function provided by Dpkg::Control::Fields. The remaining +fields are the parameters that are passed unchanged to the corresponding +function. + +Known operations are "register", "insert_after" and "insert_before". + +=item post-process-changelog-entry ($fields) + +The hook is called in Dpkg::Changelog to post-process a +Dpkg::Changelog::Entry after it has been created and filled with the +appropriate values. + +=item update-buildflags ($flags) + +The hook is called in Dpkg::BuildFlags to allow the vendor to override +the default values set for the various build flags. $flags is a +Dpkg::BuildFlags object. + +=back + +=cut + +sub run_hook { + my ($self, $hook, @params) = @_; + + if ($hook eq 'before-source-build') { + my $srcpkg = shift @params; + } elsif ($hook eq 'keyrings') { + return (); + } elsif ($hook eq 'register-custom-fields') { + return (); + } elsif ($hook eq 'post-process-changelog-entry') { + my $fields = shift @params; + } elsif ($hook eq 'extend-patch-header') { + my ($textref, $ch_info) = @params; + } elsif ($hook eq 'update-buildflags') { + my $flags = shift @params; + } + + # Default return value for unknown/unimplemented hooks + return; +} + +=back + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Vendor/Ubuntu.pm b/third_party/dpkg-dev/scripts/Dpkg/Vendor/Ubuntu.pm new file mode 100644 index 0000000..e9c2003 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Vendor/Ubuntu.pm
@@ -0,0 +1,185 @@ +# Copyright © 2008 Ian Jackson <ian@davenant.greenend.org.uk> +# Copyright © 2008 Canonical, Ltd. +# written by Colin Watson <cjwatson@ubuntu.com> +# Copyright © 2008 James Westby <jw+debian@jameswestby.net> +# Copyright © 2009 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Vendor::Ubuntu; + +use strict; +use warnings; + +our $VERSION = '0.01'; + +use Dpkg::ErrorHandling; +use Dpkg::Gettext; +use Dpkg::Path qw(find_command); +use Dpkg::Control::Types; +use Dpkg::BuildOptions; +use Dpkg::Arch qw(debarch_eq get_host_arch); + +use parent qw(Dpkg::Vendor::Debian); + +=encoding utf8 + +=head1 NAME + +Dpkg::Vendor::Ubuntu - Ubuntu vendor object + +=head1 DESCRIPTION + +This vendor object customize the behaviour of dpkg-source +to check that Maintainers have been modified if necessary. + +=cut + +sub run_hook { + my ($self, $hook, @params) = @_; + + if ($hook eq 'before-source-build') { + my $src = shift @params; + my $fields = $src->{fields}; + + # check that Maintainer/XSBC-Original-Maintainer comply to + # https://wiki.ubuntu.com/DebianMaintainerField + if (defined($fields->{'Version'}) and defined($fields->{'Maintainer'}) and + $fields->{'Version'} =~ /ubuntu/) { + if ($fields->{'Maintainer'} !~ /ubuntu/i) { + if (defined ($ENV{DEBEMAIL}) and $ENV{DEBEMAIL} =~ /\@ubuntu\.com/) { + error(_g('Version number suggests Ubuntu changes, but Maintainer: does not have Ubuntu address')); + } else { + warning(_g('Version number suggests Ubuntu changes, but Maintainer: does not have Ubuntu address')); + } + } + unless ($fields->{'Original-Maintainer'}) { + warning(_g('Version number suggests Ubuntu changes, but there is no XSBC-Original-Maintainer field')); + } + } + + } elsif ($hook eq 'keyrings') { + my @keyrings = $self->SUPER::run_hook($hook); + + push(@keyrings, '/usr/share/keyrings/ubuntu-archive-keyring.gpg'); + return @keyrings; + + } elsif ($hook eq 'register-custom-fields') { + my @field_ops = $self->SUPER::run_hook($hook); + push @field_ops, + [ 'register', 'Launchpad-Bugs-Fixed', + CTRL_FILE_CHANGES | CTRL_CHANGELOG ], + [ 'insert_after', CTRL_FILE_CHANGES, 'Closes', 'Launchpad-Bugs-Fixed' ], + [ 'insert_after', CTRL_CHANGELOG, 'Closes', 'Launchpad-Bugs-Fixed' ]; + return @field_ops; + + } elsif ($hook eq 'post-process-changelog-entry') { + my $fields = shift @params; + + # Add Launchpad-Bugs-Fixed field + my $bugs = find_launchpad_closes($fields->{'Changes'} || ''); + if (scalar(@$bugs)) { + $fields->{'Launchpad-Bugs-Fixed'} = join(' ', @$bugs); + } + + } elsif ($hook eq 'update-buildflags') { + my $flags = shift @params; + my $build_opts = Dpkg::BuildOptions->new(); + + if (!$build_opts->has('noopt')) { + if (debarch_eq(get_host_arch(), 'ppc64el')) { + for my $flag (qw(CFLAGS CXXFLAGS GCJFLAGS FFLAGS)) { + $flags->set($flag, '-g -O3', 'vendor'); + } + } + } + # Per https://wiki.ubuntu.com/DistCompilerFlags + $flags->set('LDFLAGS', '-Wl,-Bsymbolic-functions', 'vendor'); + + # Run the Debian hook to add hardening flags + $self->SUPER::run_hook($hook, $flags); + + # Allow control of hardening-wrapper via dpkg-buildpackage DEB_BUILD_OPTIONS + my $hardening; + if ($build_opts->has('hardening')) { + $hardening = $build_opts->get('hardening') // 1; + } + if ($build_opts->has('nohardening')) { + $hardening = 0; + } + if (defined $hardening) { + my $flag = 'DEB_BUILD_HARDENING'; + if ($hardening ne '0') { + if (!find_command('hardened-cc')) { + syserr(_g("'hardening' flag found but 'hardening-wrapper' not installed")); + } + if ($hardening ne '1') { + my @options = split(/,\s*/, $hardening); + $hardening = 1; + + my @hardopts = qw(format fortify stackprotector pie relro); + foreach my $item (@hardopts) { + my $upitem = uc($item); + foreach my $option (@options) { + if ($option =~ /^(no)?$item$/) { + $flags->set($flag . '_' . $upitem, + not defined $1 or $1 eq '', 'env'); + } + } + } + } + } + if (defined $ENV{$flag}) { + info(_g('overriding %s in environment: %s'), $flag, $hardening); + } + $flags->set($flag, $hardening, 'env'); + } + + } else { + return $self->SUPER::run_hook($hook, @params); + } + +} + +=head1 PUBLIC FUNCTIONS + +=over + +=item $bugs = Dpkg::Vendor::Ubuntu::find_launchpad_closes($changes) + +Takes one string as argument and finds "LP: #123456, #654321" statements, +which are references to bugs on Launchpad. Returns all closed bug +numbers in an array reference. + +=cut + +sub find_launchpad_closes { + my ($changes) = @_; + my %closes; + + while ($changes && + ($changes =~ /lp:\s+\#\d+(?:,\s*\#\d+)*/ig)) { + $closes{$_} = 1 foreach($& =~ /\#?\s?(\d+)/g); + } + + my @closes = sort { $a <=> $b } keys %closes; + + return \@closes; +} + +=back + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Version.pm b/third_party/dpkg-dev/scripts/Dpkg/Version.pm new file mode 100644 index 0000000..5461f597 --- /dev/null +++ b/third_party/dpkg-dev/scripts/Dpkg/Version.pm
@@ -0,0 +1,445 @@ +# Copyright © Colin Watson <cjwatson@debian.org> +# Copyright © Ian Jackson <iwj@debian.org> +# Copyright © 2007 Don Armstrong <don@donarmstrong.com>. +# Copyright © 2009 Raphaël Hertzog <hertzog@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Dpkg::Version; + +use strict; +use warnings; + +our $VERSION = '1.01'; + +use Dpkg::ErrorHandling; +use Dpkg::Gettext; + +use Carp; +use Exporter qw(import); +our @EXPORT = qw(version_compare version_compare_relation + version_normalize_relation version_compare_string + version_compare_part version_split_digits version_check + REL_LT REL_LE REL_EQ REL_GE REL_GT); + +use constant { + REL_LT => '<<', + REL_LE => '<=', + REL_EQ => '=', + REL_GE => '>=', + REL_GT => '>>', +}; + +use overload + '<=>' => \&comparison, + 'cmp' => \&comparison, + '""' => sub { return $_[0]->as_string(); }, + 'bool' => sub { return $_[0]->as_string() if $_[0]->is_valid(); }, + 'fallback' => 1; + +=encoding utf8 + +=head1 NAME + +Dpkg::Version - handling and comparing dpkg-style version numbers + +=head1 DESCRIPTION + +The Dpkg::Version module provides pure-Perl routines to compare +dpkg-style version numbers (as used in Debian packages) and also +an object oriented interface overriding perl operators +to do the right thing when you compare Dpkg::Version object between +them. + +=head1 OBJECT INTERFACE + +=over 4 + +=item my $v = Dpkg::Version->new($version, %opts) + +Create a new Dpkg::Version object corresponding to the version indicated in +the string (scalar) $version. By default it will accepts any string +and consider it as a valid version. If you pass the option "check => 1", +it will return undef if the version is invalid (see version_check for +details). + +You can always call $v->is_valid() later on to verify that the version is +valid. + +=cut + +sub new { + my ($this, $ver, %opts) = @_; + my $class = ref($this) || $this; + $ver = "$ver" if ref($ver); # Try to stringify objects + + if ($opts{check}) { + return unless version_check($ver); + } + + my $self = {}; + if ($ver =~ /^([^:]*):(.+)$/) { + $self->{epoch} = $1; + $ver = $2; + } else { + $self->{epoch} = 0; + $self->{no_epoch} = 1; + } + if ($ver =~ /(.*)-(.*)$/) { + $self->{version} = $1; + $self->{revision} = $2; + } else { + $self->{version} = $ver; + $self->{revision} = 0; + $self->{no_revision} = 1; + } + + return bless $self, $class; +} + +=item boolean evaluation + +When the Dpkg::Version object is used in a boolean evaluation (for example +in "if ($v)" or "$v || 'default'") it returns its string representation +if the version stored is valid ($v->is_valid()) and undef otherwise. + +=item $v->is_valid() + +Returns true if the version is valid, false otherwise. + +=cut + +sub is_valid { + my ($self) = @_; + return scalar version_check($self); +} + +=item $v->epoch(), $v->version(), $v->revision() + +Returns the corresponding part of the full version string. + +=cut + +sub epoch { + my $self = shift; + return $self->{epoch}; +} + +sub version { + my $self = shift; + return $self->{version}; +} + +sub revision { + my $self = shift; + return $self->{revision}; +} + +=item $v->is_native() + +Returns true if the version is native, false if it has a revision. + +=cut + +sub is_native { + my $self = shift; + return $self->{no_revision}; +} + +=item $v1 <=> $v2, $v1 < $v2, $v1 <= $v2, $v1 > $v2, $v1 >= $v2 + +Numerical comparison of various versions numbers. One of the two operands +needs to be a Dpkg::Version, the other one can be anything provided that +its string representation is a version number. + +=cut + +sub comparison { + my ($a, $b, $inverted) = @_; + if (not ref($b) or not $b->isa('Dpkg::Version')) { + $b = Dpkg::Version->new($b); + } + ($a, $b) = ($b, $a) if $inverted; + my $r = version_compare_part($a->epoch(), $b->epoch()); + return $r if $r; + $r = version_compare_part($a->version(), $b->version()); + return $r if $r; + return version_compare_part($a->revision(), $b->revision()); +} + +=item "$v", $v->as_string(), $v->as_string(%options) + +Accepts an optional option hash reference, affecting the string conversion. + +Options: + +=over 8 + +=item omit_epoch (defaults to 0) + +Omit the epoch, if present, in the output string. + +=item omit_revision (defaults to 0) + +Omit the revision, if present, in the output string. + +=back + +Returns the string representation of the version number. + +=cut + +sub as_string { + my ($self, %opts) = @_; + my $no_epoch = $opts{omit_epoch} || $self->{no_epoch}; + my $no_revision = $opts{omit_revision} || $self->{no_revision}; + + my $str = ''; + $str .= $self->{epoch} . ':' unless $no_epoch; + $str .= $self->{version}; + $str .= '-' . $self->{revision} unless $no_revision; + return $str; +} + +=back + +=head1 FUNCTIONS + +All the functions are exported by default. + +=over 4 + +=item version_compare($a, $b) + +Returns -1 if $a is earlier than $b, 0 if they are equal and 1 if $a +is later than $b. + +If $a or $b are not valid version numbers, it dies with an error. + +=cut + +sub version_compare($$) { + my ($a, $b) = @_; + my $va = Dpkg::Version->new($a, check => 1); + defined($va) || error(_g('%s is not a valid version'), "$a"); + my $vb = Dpkg::Version->new($b, check => 1); + defined($vb) || error(_g('%s is not a valid version'), "$b"); + return $va <=> $vb; +} + +=item version_compare_relation($a, $rel, $b) + +Returns the result (0 or 1) of the given comparison operation. This +function is implemented on top of version_compare(). + +Allowed values for $rel are the exported constants REL_GT, REL_GE, +REL_EQ, REL_LE, REL_LT. Use version_normalize_relation() if you +have an input string containing the operator. + +=cut + +sub version_compare_relation($$$) { + my ($a, $op, $b) = @_; + my $res = version_compare($a, $b); + + if ($op eq REL_GT) { + return $res > 0; + } elsif ($op eq REL_GE) { + return $res >= 0; + } elsif ($op eq REL_EQ) { + return $res == 0; + } elsif ($op eq REL_LE) { + return $res <= 0; + } elsif ($op eq REL_LT) { + return $res < 0; + } else { + croak "unsupported relation for version_compare_relation(): '$op'"; + } +} + +=item my $rel = version_normalize_relation($rel_string) + +Returns the normalized constant of the relation $rel (a value +among REL_GT, REL_GE, REL_EQ, REL_LE and REL_LT). Supported +relations names in input are: "gt", "ge", "eq", "le", "lt", ">>", ">=", +"=", "<=", "<<". ">" and "<" are also supported but should not be used as +they are obsolete aliases of ">=" and "<=". + +=cut + +sub version_normalize_relation($) { + my $op = shift; + + warning('relation %s is deprecated: use %s or %s', + $op, "$op$op", "$op=") if ($op eq '>' or $op eq '<'); + + if ($op eq '>>' or $op eq 'gt') { + return REL_GT; + } elsif ($op eq '>=' or $op eq 'ge' or $op eq '>') { + return REL_GE; + } elsif ($op eq '=' or $op eq 'eq') { + return REL_EQ; + } elsif ($op eq '<=' or $op eq 'le' or $op eq '<') { + return REL_LE; + } elsif ($op eq '<<' or $op eq 'lt') { + return REL_LT; + } else { + croak "bad relation '$op'"; + } +} + +=item version_compare_string($a, $b) + +String comparison function used for comparing non-numerical parts of version +numbers. Returns -1 if $a is earlier than $b, 0 if they are equal and 1 if $a +is later than $b. + +The "~" character always sort lower than anything else. Digits sort lower +than non-digits. Among remaining characters alphabetic characters (A-Za-z) +sort lower than the other ones. Within each range, the ASCII decimal value +of the character is used to sort between characters. + +=cut + +sub _version_order { + my ($x) = @_; + + if ($x eq '~') { + return -1; + } elsif ($x =~ /^\d$/) { + return $x * 1 + 1; + } elsif ($x =~ /^[A-Za-z]$/) { + return ord($x); + } else { + return ord($x) + 256; + } +} + +sub version_compare_string($$) { + my @a = map { _version_order($_) } split(//, shift); + my @b = map { _version_order($_) } split(//, shift); + while (1) { + my ($a, $b) = (shift @a, shift @b); + return 0 if not defined($a) and not defined($b); + $a ||= 0; # Default order for "no character" + $b ||= 0; + return 1 if $a > $b; + return -1 if $a < $b; + } +} + +=item version_compare_part($a, $b) + +Compare two corresponding sub-parts of a version number (either upstream +version or debian revision). + +Each parameter is split by version_split_digits() and resulting items +are compared together. As soon as a difference happens, it returns -1 if +$a is earlier than $b, 0 if they are equal and 1 if $a is later than $b. + +=cut + +sub version_compare_part($$) { + my @a = version_split_digits(shift); + my @b = version_split_digits(shift); + while (1) { + my ($a, $b) = (shift @a, shift @b); + return 0 if not defined($a) and not defined($b); + $a ||= 0; # Default value for lack of version + $b ||= 0; + if ($a =~ /^\d+$/ and $b =~ /^\d+$/) { + # Numerical comparison + my $cmp = $a <=> $b; + return $cmp if $cmp; + } else { + # String comparison + my $cmp = version_compare_string($a, $b); + return $cmp if $cmp; + } + } +} + +=item my @items = version_split_digits($version) + +Splits a string in items that are each entirely composed either +of digits or of non-digits. For instance for "1.024~beta1+svn234" it would +return ("1", ".", "024", "~beta", "1", "+svn", "234"). + +=cut + +sub version_split_digits($) { + return split(/(?<=\d)(?=\D)|(?<=\D)(?=\d)/, $_[0]); +} + +=item my ($ok, $msg) = version_check($version) + +=item my $ok = version_check($version) + +Checks the validity of $version as a version number. Returns 1 in $ok +if the version is valid, 0 otherwise. In the latter case, $msg +contains a description of the problem with the $version scalar. + +=cut + +sub version_check($) { + my $version = shift; + my $str; + if (defined $version) { + $str = "$version"; + $version = Dpkg::Version->new($str) unless ref($version); + } + if (not defined($str) or not length($str)) { + my $msg = _g('version number cannot be empty'); + return (0, $msg) if wantarray; + return 0; + } + if ($version->version() =~ m/^[^\d]/) { + my $msg = _g('version number does not start with digit'); + return (0, $msg) if wantarray; + return 0; + } + if ($str =~ m/([^-+:.0-9a-zA-Z~])/o) { + my $msg = sprintf(_g("version number contains illegal character `%s'"), $1); + return (0, $msg) if wantarray; + return 0; + } + if ($version->epoch() !~ /^\d*$/) { + my $msg = sprintf(_g('epoch part of the version number ' . + "is not a number: '%s'"), $version->epoch()); + return (0, $msg) if wantarray; + return 0; + } + return (1, '') if wantarray; + return 1; +} + +=back + +=head1 CHANGES + +=head2 Version 1.01 + +New argument: Accept an options argument in $v->as_string(). + +New method: $v->is_native(). + +=head1 AUTHOR + +Don Armstrong <don@donarmstrong.com>, Colin Watson +<cjwatson@debian.org> and Raphaël Hertzog <hertzog@debian.org>, based on +the implementation in F<dpkg/lib/version.c> by Ian Jackson and others. + +=cut + +1;
diff --git a/third_party/dpkg-dev/scripts/dpkg-shlibdeps.pl b/third_party/dpkg-dev/scripts/dpkg-shlibdeps.pl new file mode 100755 index 0000000..ea5cb66 --- /dev/null +++ b/third_party/dpkg-dev/scripts/dpkg-shlibdeps.pl
@@ -0,0 +1,870 @@ +#!/usr/bin/perl +# +# dpkg-shlibdeps +# +# Copyright © 1996 Ian Jackson +# Copyright © 2000 Wichert Akkerman +# Copyright © 2006 Frank Lichtenheld +# Copyright © 2006-2010,2012-2013 Guillem Jover <guillem@debian.org> +# Copyright © 2007 Raphaël Hertzog +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +use strict; +use warnings; + +use POSIX qw(:errno_h); +use Cwd qw(realpath); +use File::Basename qw(dirname); + +use Dpkg (); +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Util qw(:list); +use Dpkg::Path qw(relative_to_pkg_root guess_pkg_root_dir + check_files_are_the_same get_control_path); +use Dpkg::Version; +use Dpkg::Shlibs qw(find_library get_library_paths); +use Dpkg::Shlibs::Objdump; +use Dpkg::Shlibs::SymbolFile; +use Dpkg::Arch qw(get_host_arch); +use Dpkg::Deps; +use Dpkg::Control::Info; +use Dpkg::Control::Fields; + + +use constant { + WARN_SYM_NOT_FOUND => 1, + WARN_DEP_AVOIDABLE => 2, + WARN_NOT_NEEDED => 4, +}; + +# By increasing importance +my @depfields = qw(Suggests Recommends Depends Pre-Depends); +my $i = 0; my %depstrength = map { $_ => $i++ } @depfields; + +textdomain('dpkg-dev'); + +my $admindir = $Dpkg::ADMINDIR; +my $shlibsoverride = "$Dpkg::CONFDIR/shlibs.override"; +my $shlibsdefault = "$Dpkg::CONFDIR/shlibs.default"; +my $shlibslocal = 'debian/shlibs.local'; +my $packagetype = 'deb'; +my $dependencyfield = 'Depends'; +my $varlistfile = 'debian/substvars'; +my $varnameprefix = 'shlibs'; +my $ignore_missing_info = 0; +my $warnings = 3; +my $debug = 0; +my @exclude = (); +my @pkg_dir_to_search = (); +my $host_arch = get_host_arch(); + +my (@pkg_shlibs, @pkg_symbols, @pkg_root_dirs); +if (-d 'debian') { + push @pkg_symbols, glob 'debian/*/DEBIAN/symbols'; + push @pkg_shlibs, glob 'debian/*/DEBIAN/shlibs'; + my %uniq = map { guess_pkg_root_dir($_) => 1 } (@pkg_symbols, @pkg_shlibs); + push @pkg_root_dirs, keys %uniq; +} + +my ($stdout, %exec); +foreach (@ARGV) { + if (m/^-T(.*)$/) { + $varlistfile = $1; + } elsif (m/^-p(\w[-:0-9A-Za-z]*)$/) { + $varnameprefix = $1; + } elsif (m/^-L(.*)$/) { + $shlibslocal = $1; + } elsif (m/^-l(.*)$/) { + Dpkg::Shlibs::add_library_dir($1); + } elsif (m/^-S(.*)$/) { + push @pkg_dir_to_search, $1; + } elsif (m/^-O$/) { + $stdout = 1; + } elsif (m/^-O(.+)$/) { + $varlistfile = $1; + } elsif (m/^-(\?|-help)$/) { + usage(); exit(0); + } elsif (m/^--version$/) { + version(); exit(0); + } elsif (m/^--admindir=(.*)$/) { + $admindir = $1; + if (not -d $admindir) { + error(_g("administrative directory '%s' does not exist"), $admindir); + } + $ENV{DPKG_ADMINDIR} = $admindir; + } elsif (m/^-d(.*)$/) { + $dependencyfield = field_capitalize($1); + if (not defined $depstrength{$dependencyfield}) { + warning(_g("unrecognized dependency field '%s'"), $dependencyfield); + } + } elsif (m/^-e(.*)$/) { + if (exists $exec{$1}) { + # Affect the binary to the most important field + if ($depstrength{$dependencyfield} > $depstrength{$exec{$1}}) { + $exec{$1} = $dependencyfield; + } + } else { + $exec{$1} = $dependencyfield; + } + } elsif (m/^--ignore-missing-info$/) { + $ignore_missing_info = 1; + } elsif (m/^--warnings=(\d+)$/) { + $warnings = $1; + } elsif (m/^-t(.*)$/) { + $packagetype = $1; + } elsif (m/^-v$/) { + $debug++; + } elsif (m/^-x(.*)$/) { + push @exclude, $1; + } elsif (m/^-/) { + usageerr(_g("unknown option \`%s'"), $_); + } else { + if (exists $exec{$_}) { + # Affect the binary to the most important field + if ($depstrength{$dependencyfield} > $depstrength{$exec{$_}}) { + $exec{$_} = $dependencyfield; + } + } else { + $exec{$_} = $dependencyfield; + } + } +} +usageerr(_g('need at least one executable')) unless scalar keys %exec; + +my $control = Dpkg::Control::Info->new(); +my $fields = $control->get_source(); +my $bd_value = deps_concat($fields->{'Build-Depends'}, $fields->{'Build-Depends-Arch'}); +my $build_deps = deps_parse($bd_value, build_dep => 1, reduce_restrictions => 1); +error(_g('error occurred while parsing %s'), 'Build-Depends/Build-Depends-Arch') + unless defined $build_deps; + +my %dependencies; + +# Statictics on soname seen in the whole run (with multiple analysis of +# binaries) +my %global_soname_notfound; +my %global_soname_used; +my %global_soname_needed; + +# Symfile and objdump caches +my %symfile_cache; +my %objdump_cache; +my %symfile_has_soname_cache; + +# Used to count errors due to missing libraries +my $error_count = 0; + +my $cur_field; +foreach my $file (keys %exec) { + $cur_field = $exec{$file}; + print ">> Scanning $file (for $cur_field field)\n" if $debug; + + my $obj = Dpkg::Shlibs::Objdump::Object->new($file); + my @sonames = $obj->get_needed_libraries; + + # Load symbols files for all needed libraries (identified by SONAME) + my %libfiles; + my %altlibfiles; + my %soname_notfound; + my %alt_soname; + foreach my $soname (@sonames) { + my $lib = my_find_library($soname, $obj->{RPATH}, $obj->{format}, $file); + unless (defined $lib) { + $soname_notfound{$soname} = 1; + $global_soname_notfound{$soname} = 1; + my $msg = _g("couldn't find library %s needed by %s (ELF " . + "format: '%s'; RPATH: '%s')"); + if (scalar(split_soname($soname))) { + errormsg($msg, $soname, $file, $obj->{format}, join(':', @{$obj->{RPATH}})); + $error_count++; + } else { + warning($msg, $soname, $file, $obj->{format}, join(':', @{$obj->{RPATH}})); + } + next; + } + $libfiles{$lib} = $soname; + my $reallib = realpath($lib); + if ($reallib ne $lib) { + $altlibfiles{$reallib} = $soname; + } + print "Library $soname found in $lib\n" if $debug; + } + my $file2pkg = find_packages(keys %libfiles, keys %altlibfiles); + my $symfile = Dpkg::Shlibs::SymbolFile->new(); + my $dumplibs_wo_symfile = Dpkg::Shlibs::Objdump->new(); + my @soname_wo_symfile; + foreach my $lib (keys %libfiles) { + my $soname = $libfiles{$lib}; + + if (none { $_ ne '' } @{$file2pkg->{$lib}}) { + # The path of the library as calculated is not the + # official path of a packaged file, try to fallback on + # on the realpath() first, maybe this one is part of a package + my $reallib = realpath($lib); + if (exists $file2pkg->{$reallib}) { + $file2pkg->{$lib} = $file2pkg->{$reallib}; + } + } + if (none { $_ ne '' } @{$file2pkg->{$lib}}) { + # If the library is really not available in an installed package, + # it's because it's in the process of being built + # Empty package name will lead to consideration of symbols + # file from the package being built only + $file2pkg->{$lib} = ['']; + print "No associated package found for $lib\n" if $debug; + } + + # Load symbols/shlibs files from packages providing libraries + foreach my $pkg (@{$file2pkg->{$lib}}) { + my $symfile_path; + my $haslocaldep = 0; + if (-e $shlibslocal and + defined(extract_from_shlibs($soname, $shlibslocal))) + { + $haslocaldep = 1; + } + if ($packagetype eq 'deb' and not $haslocaldep) { + # Use fine-grained dependencies only on real deb + # and only if the dependency is not provided by shlibs.local + $symfile_path = find_symbols_file($pkg, $soname, $lib); + } + if (defined($symfile_path)) { + # Load symbol information + print "Using symbols file $symfile_path for $soname\n" if $debug; + unless (exists $symfile_cache{$symfile_path}) { + $symfile_cache{$symfile_path} = + Dpkg::Shlibs::SymbolFile->new(file => $symfile_path); + } + $symfile->merge_object_from_symfile($symfile_cache{$symfile_path}, $soname); + } + if (defined($symfile_path) && $symfile->has_object($soname)) { + # Initialize dependencies with the smallest minimal version + # of all symbols (unversioned dependency is not ok as the + # library might not have always been available in the + # package and we really need it) + my $dep = $symfile->get_dependency($soname); + my $minver = $symfile->get_smallest_version($soname) || ''; + foreach my $subdep (split /\s*,\s*/, $dep) { + if (not exists $dependencies{$cur_field}{$subdep}) { + $dependencies{$cur_field}{$subdep} = Dpkg::Version->new($minver); + print " Initialize dependency ($subdep) with minimal " . + "version ($minver)\n" if $debug > 1; + } + } + } else { + # No symbol file found, fall back to standard shlibs + print "Using shlibs+objdump for $soname (file $lib)\n" if $debug; + unless (exists $objdump_cache{$lib}) { + $objdump_cache{$lib} = Dpkg::Shlibs::Objdump::Object->new($lib); + } + my $libobj = $objdump_cache{$lib}; + my $id = $dumplibs_wo_symfile->add_object($libobj); + if (($id ne $soname) and ($id ne $lib)) { + warning(_g('%s has an unexpected SONAME (%s)'), $lib, $id); + $alt_soname{$id} = $soname; + } + push @soname_wo_symfile, $soname; + # Only try to generate a dependency for libraries with a SONAME + if ($libobj->is_public_library() and not + add_shlibs_dep($soname, $pkg, $lib)) { + # This failure is fairly new, try to be kind by + # ignoring as many cases that can be safely ignored + my $ignore = 0; + # 1/ when the lib and the binary are in the same + # package + my $root_file = guess_pkg_root_dir($file); + my $root_lib = guess_pkg_root_dir($lib); + $ignore++ if defined $root_file and defined $root_lib + and check_files_are_the_same($root_file, $root_lib); + # 2/ when the lib is not versioned and can't be + # handled by shlibs + $ignore++ unless scalar(split_soname($soname)); + # 3/ when we have been asked to do so + $ignore++ if $ignore_missing_info; + error(_g('no dependency information found for %s ' . + '(used by %s)'), $lib, $file) + unless $ignore; + } + } + } + } + + # Scan all undefined symbols of the binary and resolve to a + # dependency + my %soname_used; + foreach (@sonames) { + # Initialize statistics + $soname_used{$_} = 0; + $global_soname_used{$_} = 0 unless exists $global_soname_used{$_}; + if (exists $global_soname_needed{$_}) { + push @{$global_soname_needed{$_}}, $file; + } else { + $global_soname_needed{$_} = [ $file ]; + } + } + my $nb_warnings = 0; + my $nb_skipped_warnings = 0; + # Disable warnings about missing symbols when we have not been able to + # find all libs + my $disable_warnings = scalar(keys(%soname_notfound)); + my $in_public_dir = 1; + if (my $relname = relative_to_pkg_root($file)) { + my $parent_dir = '/' . dirname($relname); + $in_public_dir = any { $parent_dir eq $_ } get_library_paths(); + } else { + warning(_g('binaries to analyze should already be ' . + "installed in their package's directory")); + } + print "Analyzing all undefined symbols\n" if $debug > 1; + foreach my $sym ($obj->get_undefined_dynamic_symbols()) { + my $name = $sym->{name}; + if ($sym->{version}) { + $name .= '@' . "$sym->{version}"; + } else { + $name .= '@' . 'Base'; + } + print " Looking up symbol $name\n" if $debug > 1; + my %symdep = $symfile->lookup_symbol($name, \@sonames); + if (keys %symdep) { + my $depends = $symfile->get_dependency($symdep{soname}, + $symdep{symbol}{dep_id}); + print " Found in symbols file of $symdep{soname} (minver: " . + "$symdep{symbol}{minver}, dep: $depends)\n" if $debug > 1; + $soname_used{$symdep{soname}}++; + $global_soname_used{$symdep{soname}}++; + if (exists $alt_soname{$symdep{soname}}) { + # Also count usage on alternate soname + $soname_used{$alt_soname{$symdep{soname}}}++; + $global_soname_used{$alt_soname{$symdep{soname}}}++; + } + update_dependency_version($depends, $symdep{symbol}{minver}); + } else { + my $syminfo = $dumplibs_wo_symfile->locate_symbol($name); + if (not defined($syminfo)) { + print " Not found\n" if $debug > 1; + next unless ($warnings & WARN_SYM_NOT_FOUND); + next if $disable_warnings; + # Complain about missing symbols only for executables + # and public libraries + if ($obj->is_executable() or $obj->is_public_library()) { + my $print_name = $name; + # Drop the default suffix for readability + $print_name =~ s/\@Base$//; + unless ($sym->{weak}) { + if ($debug or ($in_public_dir and $nb_warnings < 10) + or (not $in_public_dir and $nb_warnings < 1)) + { + if ($in_public_dir) { + warning(_g('symbol %s used by %s found in none of the ' . + 'libraries'), $print_name, $file); + } else { + warning(_g('%s contains an unresolvable reference to ' . + "symbol %s: it's probably a plugin"), + $file, $print_name); + } + $nb_warnings++; + } else { + $nb_skipped_warnings++; + } + } + } + } else { + print " Found in $syminfo->{soname} ($syminfo->{objid})\n" if $debug > 1; + if (exists $alt_soname{$syminfo->{soname}}) { + # Also count usage on alternate soname + $soname_used{$alt_soname{$syminfo->{soname}}}++; + $global_soname_used{$alt_soname{$syminfo->{soname}}}++; + } + $soname_used{$syminfo->{soname}}++; + $global_soname_used{$syminfo->{soname}}++; + } + } + } + warning(P_('%d similar warning has been skipped (use -v to see it)', + '%d other similar warnings have been skipped (use -v to see ' . + 'them all)', $nb_skipped_warnings), $nb_skipped_warnings) + if $nb_skipped_warnings; + foreach my $soname (@sonames) { + # Adjust minimal version of dependencies with information + # extracted from build-dependencies + my $dev_pkg = $symfile->get_field($soname, 'Build-Depends-Package'); + if (defined $dev_pkg) { + print "Updating dependencies of $soname with build-dependencies\n" if $debug; + my $minver = get_min_version_from_deps($build_deps, $dev_pkg); + if (defined $minver) { + foreach my $dep ($symfile->get_dependencies($soname)) { + update_dependency_version($dep, $minver, 1); + print " Minimal version of $dep updated with $minver\n" if $debug; + } + } else { + print " No minimal version found in $dev_pkg build-dependency\n" if $debug; + } + } + + # Warn about un-NEEDED libraries + unless ($soname_notfound{$soname} or $soname_used{$soname}) { + # Ignore warning for libm.so.6 if also linked against libstdc++ + next if ($soname =~ /^libm\.so\.\d+$/ and + any { m/^libstdc\+\+\.so\.\d+/ } @sonames); + next unless ($warnings & WARN_NOT_NEEDED); + warning(_g('%s should not be linked against %s (it uses none of ' . + "the library's symbols)"), $file, $soname); + } + } +} + +# Warn of unneeded libraries at the "package" level (i.e. over all +# binaries that we have inspected) +foreach my $soname (keys %global_soname_needed) { + unless ($global_soname_notfound{$soname} or $global_soname_used{$soname}) { + next if ($soname =~ /^libm\.so\.\d+$/ and + any { m/^libstdc\+\+\.so\.\d+/ } keys %global_soname_needed); + next unless ($warnings & WARN_DEP_AVOIDABLE); + warning(P_('package could avoid a useless dependency if %s was not ' . + "linked against %s (it uses none of the library's symbols)", + 'package could avoid a useless dependency if %s were not ' . + "linked against %s (they use none of the library's symbols)", + scalar @{$global_soname_needed{$soname}}), + join(' ', @{$global_soname_needed{$soname}}), $soname); + } +} + +# Quit now if any missing libraries +if ($error_count >= 1) { + my $note = _g('Note: libraries are not searched in other binary packages ' . + "that do not have any shlibs or symbols file.\nTo help dpkg-shlibdeps " . + 'find private libraries, you might need to use -l.'); + error(P_('cannot continue due to the error above', + 'cannot continue due to the errors listed above', + $error_count) . "\n" . $note); +} + +# Open substvars file +my $fh; +if ($stdout) { + $fh = \*STDOUT; +} else { + open(my $new_fh, '>', "$varlistfile.new") + or syserr(_g("open new substvars file \`%s'"), "$varlistfile.new"); + if (-e $varlistfile) { + open(my $old_fh, '<', $varlistfile) + or syserr(_g("open old varlist file \`%s' for reading"), $varlistfile); + while (my $entry = <$old_fh>) { + next if $entry =~ m/^\Q$varnameprefix\E:/; + print { $new_fh } $entry + or syserr(_g("copy old entry to new varlist file \`%s'"), + "$varlistfile.new"); + } + close($old_fh); + } + $fh = $new_fh; +} + +# Write out the shlibs substvars +my %depseen; + +sub filter_deps { + my ($dep, $field) = @_; + # Skip dependencies on excluded packages + foreach my $exc (@exclude) { + return 0 if $dep =~ /^\s*\Q$exc\E\b/; + } + # Don't include dependencies if they are already + # mentionned in a higher priority field + if (not exists($depseen{$dep})) { + $depseen{$dep} = $dependencies{$field}{$dep}; + return 1; + } else { + # Since dependencies can be versionned, we have to + # verify if the dependency is stronger than the + # previously seen one + my $stronger; + if ($depseen{$dep} eq $dependencies{$field}{$dep}) { + # If both versions are the same (possibly unversionned) + $stronger = 0; + } elsif ($dependencies{$field}{$dep} eq '') { + $stronger = 0; # If the dep is unversionned + } elsif ($depseen{$dep} eq '') { + $stronger = 1; # If the dep seen is unversionned + } elsif (version_compare_relation($depseen{$dep}, REL_GT, + $dependencies{$field}{$dep})) { + # The version of the dep seen is stronger... + $stronger = 0; + } else { + $stronger = 1; + } + $depseen{$dep} = $dependencies{$field}{$dep} if $stronger; + return $stronger; + } +} + +foreach my $field (reverse @depfields) { + my $dep = ''; + if (exists $dependencies{$field} and scalar keys %{$dependencies{$field}}) { + $dep = join ', ', + map { + # Translate dependency templates into real dependencies + if ($dependencies{$field}{$_}) { + s/#MINVER#/(>= $dependencies{$field}{$_})/g; + } else { + s/#MINVER#//g; + } + s/\s+/ /g; + $_; + } grep { filter_deps($_, $field) } + keys %{$dependencies{$field}}; + } + if ($dep) { + my $obj = deps_parse($dep); + error(_g('invalid dependency got generated: %s'), $dep) unless defined $obj; + $obj->sort(); + print { $fh } "$varnameprefix:$field=$obj\n"; + } +} + +# Replace old file by new one +if (!$stdout) { + close($fh) or syserr(_g('cannot close %s'), "$varlistfile.new"); + rename("$varlistfile.new",$varlistfile) + or syserr(_g("install new varlist file \`%s'"), $varlistfile); +} + +## +## Functions +## + +sub version { + printf _g("Debian %s version %s.\n"), $Dpkg::PROGNAME, $Dpkg::PROGVERSION; + + printf _g(' + This is free software; see the GNU General Public License version 2 or + later for copying conditions. There is NO warranty. + '); +} + +sub usage { + printf _g( + 'Usage: %s [<option>...] <executable>|-e<executable> [<option>...]') + . "\n\n" . _g( + "Positional options (order is significant): + <executable> include dependencies for <executable>, + -e<executable> (use -e if <executable> starts with '-') + -d<dependency-field> next executable(s) set shlibs:<dependency-field>.") + . "\n\n" . _g( + "Options: + -l<library-dir> add directory to private shared library search list. + -p<varname-prefix> set <varname-prefix>:* instead of shlibs:*. + -O[<file>] write variable settings to stdout (or <file>). + -L<local-shlibs-file> shlibs override file, not debian/shlibs.local. + -T<substvars-file> update variables here, not debian/substvars. + -t<type> set package type (default is deb). + -x<package> exclude package from the generated dependencies. + -S<package-build-dir> search needed libraries in the given + package build directory first. + -v enable verbose mode (can be used multiple times). + --ignore-missing-info don't fail if dependency information can't be found. + --warnings=<value> define set of active warnings (see manual page). + --admindir=<directory> change the administrative directory. + -?, --help show this help message. + --version show the version.") + . "\n\n" . _g( + 'Dependency fields recognized are: + %s + '), $Dpkg::PROGNAME, join('/', @depfields); +} + +sub get_min_version_from_deps { + my ($dep, $pkg) = @_; + if ($dep->isa('Dpkg::Deps::Simple')) { + if (($dep->{package} eq $pkg) && + defined($dep->{relation}) && + (($dep->{relation} eq REL_GE) || + ($dep->{relation} eq REL_GT))) + { + return $dep->{version}; + } + return; + } else { + my $res; + foreach my $subdep ($dep->get_deps()) { + my $minver = get_min_version_from_deps($subdep, $pkg); + next if not defined $minver; + if (defined $res) { + if (version_compare_relation($minver, REL_GT, $res)) { + $res = $minver; + } + } else { + $res = $minver; + } + } + return $res; + } +} + +sub update_dependency_version { + my ($dep, $minver, $existing_only) = @_; + return if not defined($minver); + $minver = Dpkg::Version->new($minver); + foreach my $subdep (split /\s*,\s*/, $dep) { + if (exists $dependencies{$cur_field}{$subdep} and + defined($dependencies{$cur_field}{$subdep})) + { + if ($dependencies{$cur_field}{$subdep} eq '' or + version_compare_relation($minver, REL_GT, + $dependencies{$cur_field}{$subdep})) + { + $dependencies{$cur_field}{$subdep} = $minver; + } + } elsif (!$existing_only) { + $dependencies{$cur_field}{$subdep} = $minver; + } + } +} + +sub add_shlibs_dep { + my ($soname, $pkg, $libfile) = @_; + my @shlibs = ($shlibslocal, $shlibsoverride); + if ($pkg eq '') { + # If the file is not packaged, try to find out the shlibs file in + # the package being built where the lib has been found + my $pkg_root = guess_pkg_root_dir($libfile); + if (defined $pkg_root) { + push @shlibs, "$pkg_root/DEBIAN/shlibs"; + } + # Fallback to other shlibs files but it shouldn't be necessary + push @shlibs, @pkg_shlibs; + } else { + my $control_file = get_control_path($pkg, 'shlibs'); + push @shlibs, $control_file if defined $control_file; + } + push @shlibs, $shlibsdefault; + print " Looking up shlibs dependency of $soname provided by '$pkg'\n" if $debug; + foreach my $file (@shlibs) { + next if not -e $file; + my $dep = extract_from_shlibs($soname, $file); + if (defined($dep)) { + print " Found $dep in $file\n" if $debug; + foreach (split(/,\s*/, $dep)) { + # Note: the value is empty for shlibs based dependency + # symbol based dependency will put a valid version as value + $dependencies{$cur_field}{$_} = Dpkg::Version->new(''); + } + return 1; + } + } + print " Found nothing\n" if $debug; + return 0; +} + +sub split_soname { + my $soname = shift; + if ($soname =~ /^(.*)\.so\.(.*)$/) { + return wantarray ? ($1, $2) : 1; + } elsif ($soname =~ /^(.*)-(\d.*)\.so$/) { + return wantarray ? ($1, $2) : 1; + } else { + return wantarray ? () : 0; + } +} + +sub extract_from_shlibs { + my ($soname, $shlibfile) = @_; + # Split soname in name/version + my ($libname, $libversion) = split_soname($soname); + unless (defined $libname) { + warning(_g("can't extract name and version from library name '%s'"), + $soname); + return; + } + # Open shlibs file + open(my $shlibs_fh, '<', $shlibfile) + or syserr(_g("unable to open shared libs info file \`%s'"), $shlibfile); + my $dep; + while (<$shlibs_fh>) { + s/\s*\n$//; + next if m/^\#/; + if (!m/^\s*(?:(\S+):\s+)?(\S+)\s+(\S+)(?:\s+(\S.*\S))?\s*$/) { + warning(_g("shared libs info file \`%s' line %d: bad line \`%s'"), + $shlibfile, $., $_); + next; + } + my $depread = defined($4) ? $4 : ''; + if (($libname eq $2) && ($libversion eq $3)) { + # Define dep and end here if the package type explicitly + # matches. Otherwise if the packagetype is not specified, use + # the dep only as a default that can be overriden by a later + # line + if (defined($1)) { + if ($1 eq $packagetype) { + $dep = $depread; + last; + } + } else { + $dep //= $depread; + } + } + } + close($shlibs_fh); + return $dep; +} + +sub find_symbols_file { + my ($pkg, $soname, $libfile) = @_; + my @files; + if ($pkg eq '') { + # If the file is not packaged, try to find out the symbols file in + # the package being built where the lib has been found + my $pkg_root = guess_pkg_root_dir($libfile); + if (defined $pkg_root) { + push @files, "$pkg_root/DEBIAN/symbols"; + } + # Fallback to other symbols files but it shouldn't be necessary + push @files, @pkg_symbols; + } else { + push @files, "$Dpkg::CONFDIR/symbols/$pkg.symbols.$host_arch", + "$Dpkg::CONFDIR/symbols/$pkg.symbols"; + my $control_file = get_control_path($pkg, 'symbols'); + push @files, $control_file if defined $control_file; + } + + foreach my $file (@files) { + if (-e $file and symfile_has_soname($file, $soname)) { + return $file; + } + } + return; +} + +sub symfile_has_soname { + my ($file, $soname) = @_; + + if (exists $symfile_has_soname_cache{$file}{$soname}) { + return $symfile_has_soname_cache{$file}{$soname}; + } + + open(my $symfile_fh, '<', $file) + or syserr(_g('cannot open file %s'), $file); + my $result = 0; + while (<$symfile_fh>) { + if (/^\Q$soname\E /) { + $result = 1; + last; + } + } + close($symfile_fh); + $symfile_has_soname_cache{$file}{$soname} = $result; + return $result; +} + +# find_library ($soname, \@rpath, $format) +sub my_find_library { + my ($lib, $rpath, $format, $execfile) = @_; + my $file; + + # Create real RPATH in case $ORIGIN is used + # Note: ld.so also supports $PLATFORM and $LIB but they are + # used in real case (yet) + my $libdir = relative_to_pkg_root($execfile); + my $origin; + if (defined $libdir) { + $origin = "/$libdir"; + $origin =~ s{/+[^/]*$}{}; + } + my @RPATH = (); + foreach my $path (@{$rpath}) { + if ($path =~ /\$ORIGIN|\$\{ORIGIN\}/) { + if (defined $origin) { + $path =~ s/\$ORIGIN/$origin/g; + $path =~ s/\$\{ORIGIN\}/$origin/g; + } else { + warning(_g('$ORIGIN is used in RPATH of %s and the corresponding ' . + 'directory could not be identified due to lack of DEBIAN ' . + "sub-directory in the root of package's build tree"), $execfile); + } + } + push @RPATH, $path; + } + + # Look into the packages we're currently building in the following + # order: + # - package build tree of the binary which is analyzed + # - package build tree given on the command line (option -S) + # - other package build trees that contain either a shlibs or a + # symbols file + my @builddirs; + my $pkg_root = guess_pkg_root_dir($execfile); + push @builddirs, $pkg_root if defined $pkg_root; + push @builddirs, @pkg_dir_to_search; + push @builddirs, @pkg_root_dirs; + my %dir_checked; + foreach my $builddir (@builddirs) { + next if defined($dir_checked{$builddir}); + $file = find_library($lib, \@RPATH, $format, $builddir); + return $file if defined($file); + $dir_checked{$builddir} = 1; + } + + # Fallback in the root directory if we have not found what we were + # looking for in the packages + $file = find_library($lib, \@RPATH, $format, ''); + return $file if defined($file); + + return; +} + +my %cached_pkgmatch = (); + +sub find_packages { + my @files; + my $pkgmatch = {}; + + foreach (@_) { + if (exists $cached_pkgmatch{$_}) { + $pkgmatch->{$_} = $cached_pkgmatch{$_}; + } else { + push @files, $_; + $cached_pkgmatch{$_} = ['']; # placeholder to cache misses too. + $pkgmatch->{$_} = ['']; # might be replaced later on + } + } + return $pkgmatch unless scalar(@files); + + my $pid = open(my $dpkg_fh, '-|'); + syserr(_g('cannot fork for %s'), 'dpkg --search') unless defined($pid); + if (!$pid) { + # Child process running dpkg --search and discarding errors + close STDERR; + open STDERR, '>', '/dev/null' + or syserr(_g('cannot open file %s'), '/dev/null'); + $ENV{LC_ALL} = 'C'; + exec('dpkg', '--search', '--', @files) + or syserr(_g('unable to execute %s'), 'dpkg'); + } + while (defined($_ = <$dpkg_fh>)) { + chomp($_); + if (m/^local diversion |^diversion by/) { + warning(_g('diversions involved - output may be incorrect')); + print { *STDERR } " $_\n" + or syserr(_g('write diversion info to stderr')); + } elsif (m/^([-a-z0-9+.:, ]+): (\/.*)$/) { + $cached_pkgmatch{$2} = $pkgmatch->{$2} = [ split(/, /, $1) ]; + } else { + warning(_g("unknown output from dpkg --search: '%s'"), $_); + } + } + close($dpkg_fh); + return $pkgmatch; +}
diff --git a/third_party/dpkg-dev/triplettable b/third_party/dpkg-dev/triplettable new file mode 100644 index 0000000..a2c683f2 --- /dev/null +++ b/third_party/dpkg-dev/triplettable
@@ -0,0 +1,31 @@ +# Bidirectional mapping between a Debian triplet and a Debian arch. +# +# Supported variables: <cpu> +# +# <Debian triplet> <Debian arch> +uclibceabi-linux-arm uclibc-linux-armel +uclibc-linux-<cpu> uclibc-linux-<cpu> +musleabihf-linux-arm musl-linux-armhf +musl-linux-<cpu> musl-linux-<cpu> +gnueabihf-linux-arm armhf +gnueabi-linux-arm armel +gnuabin32-linux-mips64el mipsn32el +gnuabin32-linux-mips64 mipsn32 +gnuabi64-linux-mips64el mips64el +gnuabi64-linux-mips64 mips64 +gnuspe-linux-powerpc powerpcspe +gnux32-linux-amd64 x32 +gnulp-linux-i386 lpia +gnu-linux-<cpu> <cpu> +gnu-kfreebsd-<cpu> kfreebsd-<cpu> +gnu-knetbsd-<cpu> knetbsd-<cpu> +gnu-kopensolaris-<cpu> kopensolaris-<cpu> +gnu-hurd-<cpu> hurd-<cpu> +bsd-freebsd-<cpu> freebsd-<cpu> +bsd-openbsd-<cpu> openbsd-<cpu> +bsd-netbsd-<cpu> netbsd-<cpu> +bsd-darwin-<cpu> darwin-<cpu> +sysv-solaris-<cpu> solaris-<cpu> +uclibceabi-uclinux-arm uclinux-armel +uclibc-uclinux-<cpu> uclinux-<cpu> +tos-mint-m68k mint-m68k
diff --git a/tools/checklicenses/checklicenses.py b/tools/checklicenses/checklicenses.py index 3d27b0f2..cfb09bc7 100755 --- a/tools/checklicenses/checklicenses.py +++ b/tools/checklicenses/checklicenses.py
@@ -231,6 +231,11 @@ 'UNKNOWN', ], + # https://bugs.chromium.org/p/chromium/issues/detail?id=655755 + 'third_party/dpkg-dev': [ + 'GPL (v2 or later)', + ], + 'third_party/devscripts': [ 'GPL (v2 or later)', ],
diff --git a/tools/metrics/histograms/extract_histograms.py b/tools/metrics/histograms/extract_histograms.py index b8e49ad..c8da0ccb 100644 --- a/tools/metrics/histograms/extract_histograms.py +++ b/tools/metrics/histograms/extract_histograms.py
@@ -63,6 +63,9 @@ MAX_HISTOGRAM_SUFFIX_DEPENDENCY_DEPTH = 5 +DEFAULT_BASE_HISTOGRAM_OBSOLETE_REASON = ( + 'Base histogram. Use suffixes of this histogram instead.') + class Error(Exception): pass @@ -230,6 +233,14 @@ return owners +def _ProcessBaseHistogramAttribute(node, histogram_entry): + if node.hasAttribute('base'): + is_base = node.getAttribute('base').lower() == 'true' + histogram_entry['base'] = is_base + if is_base and 'obsolete' not in histogram_entry: + histogram_entry['obsolete'] = DEFAULT_BASE_HISTOGRAM_OBSOLETE_REASON + + def _ExtractHistogramsFromXmlTree(tree, enums): """Extract all <histogram> nodes in the tree into a dictionary.""" @@ -288,6 +299,8 @@ else: histogram_entry['enum'] = enums[enum_name] + _ProcessBaseHistogramAttribute(histogram, histogram_entry) + return histograms, have_errors @@ -398,8 +411,17 @@ new_histogram_name = _ExpandHistogramNameWithSuffixes( suffix_name, histogram_name, histogram_suffixes) if new_histogram_name != histogram_name: - histograms[new_histogram_name] = copy.deepcopy( - histograms[histogram_name]) + new_histogram = copy.deepcopy(histograms[histogram_name]) + # Do not copy forward base histogram state to suffixed + # histograms. Any suffixed histograms that wish to remain base + # histograms must explicitly re-declare themselves as base + # histograms. + if new_histogram.get('base', False): + del new_histogram['base'] + if (new_histogram.get('obsolete', '') == + DEFAULT_BASE_HISTOGRAM_OBSOLETE_REASON): + del new_histogram['obsolete'] + histograms[new_histogram_name] = new_histogram suffix_label = suffix_labels.get(suffix_name, '') @@ -436,6 +458,8 @@ if obsolete_reason: histograms[new_histogram_name]['obsolete'] = obsolete_reason + _ProcessBaseHistogramAttribute(suffix, histograms[new_histogram_name]) + except Error: have_errors = True
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index cf35fd4..8428fb9f 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -6827,6 +6827,60 @@ </summary> </histogram> +<histogram name="Conflicts.ConfirmedBadModules" units="modules"> + <owner>chrisha@chromium.org</owner> + <summary> + The number of confirmed bad modules found during module enumeration. + </summary> +</histogram> + +<histogram name="Conflicts.EnumerateLoadedModules" units="ms"> + <owner>chrisha@chromium.org</owner> + <summary> + The length of time it takes to enumerate the loaded modules in the browser + process. + </summary> +</histogram> + +<histogram name="Conflicts.EnumerateShellExtensions" units="ms"> + <owner>chrisha@chromium.org</owner> + <summary> + The length of time it takes to enumerate the shell extensions. + </summary> +</histogram> + +<histogram name="Conflicts.EnumerateWinsockModules" units="ms"> + <owner>chrisha@chromium.org</owner> + <summary> + The length of time it takes to enumerate the Winsock LSP modules. + </summary> +</histogram> + +<histogram name="Conflicts.EnumerationInspectionTime" units="ms"> + <owner>chrisha@chromium.org</owner> + <summary> + The cumulative length of time it takes to inspect all modules on disk, + extracting their certificates and version information. This work is actually + spread out over a much longer period of time so as not to impact the user. + </summary> +</histogram> + +<histogram name="Conflicts.EnumerationTotalTime" units="ms"> + <owner>chrisha@chromium.org</owner> + <summary> + The cumulative length of time it takes to enumerate and inspect all modules. + This work is actually spread out over a much longer period of time so as not + to impact the user. + </summary> +</histogram> + +<histogram name="Conflicts.SuspectedBadModules" units="modules"> + <owner>chrisha@chromium.org</owner> + <summary> + The number of suspected bad modules found during module enumeration. + </summary> +</histogram> + <histogram name="ConnectivityDiagnostics.ChromeOsSignalStrength" units="%"> <owner>ebeach@google.com</owner> <summary> @@ -21427,6 +21481,21 @@ <histogram name="interstitial.ssl.clockstate.network2" enum="NetworkClockStates"> + <obsolete> + Deprecated October 2016. Due to a bug, data in this histogram is mislabelled + and should be disregarded. Replaced with + interstitial.ssl.clockstate.network3. + </obsolete> + <owner>estark@chromium.org</owner> + <owner>mab@chromium.org</owner> + <summary> + State of the system clock, relative to network time, when an SSL + CERT_INVALID_DATE error is seen. + </summary> +</histogram> + +<histogram name="interstitial.ssl.clockstate.network3" + enum="NetworkClockStates"> <owner>estark@chromium.org</owner> <owner>mab@chromium.org</owner> <summary> @@ -40306,7 +40375,7 @@ </summary> </histogram> -<histogram name="PageLoad.AbortTiming.Background" units="ms"> +<histogram base="true" name="PageLoad.AbortTiming.Background" units="ms"> <owner>bmcquade@chromium.org</owner> <owner>csharrison@chromium.org</owner> <summary> @@ -40316,7 +40385,7 @@ </summary> </histogram> -<histogram name="PageLoad.AbortTiming.ClientRedirect" units="ms"> +<histogram base="true" name="PageLoad.AbortTiming.ClientRedirect" units="ms"> <owner>csharrison@chromium.org</owner> <summary> This metric is still experimental and not yet ready to be relied upon. @@ -40325,7 +40394,7 @@ </summary> </histogram> -<histogram name="PageLoad.AbortTiming.Close" units="ms"> +<histogram base="true" name="PageLoad.AbortTiming.Close" units="ms"> <owner>csharrison@chromium.org</owner> <summary> This metric is still experimental and not yet ready to be relied upon. @@ -40334,7 +40403,8 @@ </summary> </histogram> -<histogram name="PageLoad.AbortTiming.ForwardBackNavigation" units="ms"> +<histogram base="true" name="PageLoad.AbortTiming.ForwardBackNavigation" + units="ms"> <owner>csharrison@chromium.org</owner> <summary> This metric is still experimental and not yet ready to be relied upon. @@ -40343,7 +40413,7 @@ </summary> </histogram> -<histogram name="PageLoad.AbortTiming.NewNavigation" units="ms"> +<histogram base="true" name="PageLoad.AbortTiming.NewNavigation" units="ms"> <owner>csharrison@chromium.org</owner> <summary> This metric is still experimental and not yet ready to be relied upon. @@ -40352,7 +40422,7 @@ </summary> </histogram> -<histogram name="PageLoad.AbortTiming.Other" units="ms"> +<histogram base="true" name="PageLoad.AbortTiming.Other" units="ms"> <owner>csharrison@chromium.org</owner> <summary> This metric is still experimental and not yet ready to be relied upon. @@ -40361,7 +40431,7 @@ </summary> </histogram> -<histogram name="PageLoad.AbortTiming.Reload" units="ms"> +<histogram base="true" name="PageLoad.AbortTiming.Reload" units="ms"> <owner>csharrison@chromium.org</owner> <summary> This metric is still experimental and not yet ready to be relied upon. @@ -40370,7 +40440,7 @@ </summary> </histogram> -<histogram name="PageLoad.AbortTiming.Stop" units="ms"> +<histogram base="true" name="PageLoad.AbortTiming.Stop" units="ms"> <owner>csharrison@chromium.org</owner> <summary> This metric is still experimental and not yet ready to be relied upon. @@ -73726,7 +73796,6 @@ <int value="140" label="ARH_CREATED_STREAM_WITHOUT_AUTHORIZATION"/> <int value="141" label="MDDH_INVALID_DEVICE_TYPE_REQUEST"/> <int value="142" label="MDDH_UNAUTHORIZED_ORIGIN"/> - <int value="143" label="NMF_INVALID_ID_CLOSE"/> </enum> <enum name="BadMessageReasonExtensions" type="int"> @@ -89065,6 +89134,7 @@ label="disable-infobar-for-protected-media-identifier"/> <int value="-1874908826" label="enable-instant-search-clicks"/> <int value="-1872989945" label="enable-webview-based-signin"/> + <int value="-1872867546" label="EnumerateAudioDevices:disabled"/> <int value="-1870961970" label="enable-filemanager-mtp"/> <int value="-1869845022" label="force-show-update-menu-item"/> <int value="-1867382602" label="WebRTC-H264WithOpenH264FFmpeg:enabled"/> @@ -89231,6 +89301,7 @@ <int value="-1052415111" label="malware-interstitial-v2"/> <int value="-1052219252" label="disable-captive-portal-bypass-proxy"/> <int value="-1041650038" label="enable-forced-migration-to-tabbed-mode"/> + <int value="-1039889738" label="NativeNotifications:enabled"/> <int value="-1039555838" label="GamepadExtensions:enabled"/> <int value="-1033738911" label="enable-mac-views-dialogs"/> <int value="-1028733699" label="MacViewsWebUIDialogs:disabled"/> @@ -89675,6 +89746,7 @@ <int value="1253698118" label="ash-disable-stable-overview-order"/> <int value="1257980502" label="disable-accelerated-video-decode"/> <int value="1268470658" label="disable-android-password-link"/> + <int value="1269940659" label="EnumerateAudioDevices:enabled"/> <int value="1272699563" label="enable-hosted-mode"/> <int value="1276209777" label="ntp-switch-to-existing-tab"/> <int value="1279584261" label="enable-carrier-switching"/> @@ -89683,6 +89755,7 @@ <int value="1294131571" label="disable-winrt-midi-api"/> <int value="1298981651" label="disable-new-task-manager"/> <int value="1300282719" label="OfflinePagesBackgroundLoading:enabled"/> + <int value="1302421166" label="NativeNotifications:disabled"/> <int value="1308537004" label="force-pnacl-subzero"/> <int value="1312025202" label="NTPOfflinePageSuggestions:disabled"/> <int value="1317562265" label="SeccompSandboxAndroid:disabled"/> @@ -108283,8 +108356,8 @@ <histogram_suffixes name="PageLoadEventConditions" separator="."> <suffix name="BeforeCommit"/> <suffix name="AfterCommit.BeforePaint"/> - <suffix name="AfterPaint.BeforeInteraction"/> - <suffix name="AfterPaint.Before1sDelayedInteraction"/> + <suffix base="true" name="AfterPaint.BeforeInteraction"/> + <suffix base="true" name="AfterPaint.Before1sDelayedInteraction"/> <suffix name="DuringParse"/> <affected-histogram name="PageLoad.AbortTiming.Background"/> <affected-histogram name="PageLoad.AbortTiming.ClientRedirect"/> @@ -109597,6 +109670,41 @@ <affected-histogram name="PreloadScanner.Counts2.Miss"/> </histogram_suffixes> +<histogram_suffixes name="SafeBrowsing.V4Store.Metrics" separator="." + ordering="prefix"> + <suffix name="UrlMalware"/> + <suffix name="UrlSoceng"/> + <suffix name="UrlUws"/> + <affected-histogram + name="SafeBrowsing.V4ProcessFullUpdate.ApplyUpdate.Result"/> + <affected-histogram name="SafeBrowsing.V4ProcessFullUpdate.ApplyUpdate.Time"/> + <affected-histogram + name="SafeBrowsing.V4ProcessFullUpdate.DecodeAdditions.Result"/> + <affected-histogram + name="SafeBrowsing.V4ProcessFullUpdate.DecodeAdditions.Time"/> + <affected-histogram name="SafeBrowsing.V4ProcessFullUpdate.MergeUpdate.Time"/> + <affected-histogram + name="SafeBrowsing.V4ProcessPartialUpdate.ApplyUpdate.Result"/> + <affected-histogram + name="SafeBrowsing.V4ProcessPartialUpdate.ApplyUpdate.Time"/> + <affected-histogram + name="SafeBrowsing.V4ProcessPartialUpdate.DecodeAdditions.Result"/> + <affected-histogram + name="SafeBrowsing.V4ProcessPartialUpdate.DecodeAdditions.Time"/> + <affected-histogram + name="SafeBrowsing.V4ProcessPartialUpdate.DecodeRemovals.Result"/> + <affected-histogram + name="SafeBrowsing.V4ProcessPartialUpdate.DecodeRemovals.Time"/> + <affected-histogram + name="SafeBrowsing.V4ProcessPartialUpdate.MergeUpdate.Time"/> + <affected-histogram name="SafeBrowsing.V4ReadFromDisk.ApplyUpdate.Result"/> + <affected-histogram name="SafeBrowsing.V4ReadFromDisk.ApplyUpdate.Time"/> + <affected-histogram + name="SafeBrowsing.V4ReadFromDisk.DecodeAdditions.Result"/> + <affected-histogram name="SafeBrowsing.V4ReadFromDisk.DecodeAdditions.Time"/> + <affected-histogram name="SafeBrowsing.V4ReadFromDisk.MergeUpdate.Time"/> +</histogram_suffixes> + <histogram_suffixes name="SafeBrowsingLists" separator="."> <suffix name="Browse" label="Browse"/> <suffix name="Download" label="Download"/>
diff --git a/tools/metrics/histograms/print_style.py b/tools/metrics/histograms/print_style.py index ee2025ce..ebd65b9 100644 --- a/tools/metrics/histograms/print_style.py +++ b/tools/metrics/histograms/print_style.py
@@ -22,7 +22,7 @@ 'enums': [], # TODO(yiyaoliu): Remove fieldtrial related pieces when it is not used. 'fieldtrial': ['name', 'separator', 'ordering'], - 'histogram': ['name', 'enum', 'units'], + 'histogram': ['base', 'name', 'enum', 'units'], 'histogram-configuration': ['logsource'], 'histogram_suffixes': ['name', 'separator', 'ordering'], 'histogram_suffixes_list': [], @@ -31,7 +31,7 @@ 'group': ['name', 'label'], 'obsolete': [], 'owner': [], - 'suffix': ['name', 'label'], + 'suffix': ['base', 'name', 'label'], 'summary': [], 'with-group': ['name'], 'with-suffix': ['name'],
diff --git a/tools/perf/benchmarks/battor.py b/tools/perf/benchmarks/battor.py index 052edd85..aeb863c 100644 --- a/tools/perf/benchmarks/battor.py +++ b/tools/perf/benchmarks/battor.py
@@ -2,8 +2,6 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import os - from core import perf_benchmark from telemetry.timeline import chrome_trace_category_filter from telemetry.web_perf import timeline_based_measurement @@ -23,11 +21,7 @@ # TODO(charliea): Reenable the CPU tracing agent once it no longer causes # indefinite hangs on Windows. # https://crbug.com/647443 - # TODO(charliea): Reenable BattOr tracing on the main perf waterfall once - # the BattOrs stop crashing as their SD cards fill up. - # crbug.com/652384 - options.config.enable_battor_trace = ( - os.environ.get('BUILDBOT_MASTERNAME') == 'chromium.perf.fyi') + options.config.enable_battor_trace = True options.config.enable_chrome_trace = True options.config.enable_atrace_trace = True options.config.atrace_config.categories = ['sched'] @@ -79,11 +73,7 @@ def CreateTimelineBasedMeasurementOptions(self): options = timeline_based_measurement.Options() - # TODO(charliea): Reenable BattOr tracing on the main perf waterfall once - # the BattOrs stop crashing as their SD cards fill up. - # crbug.com/652384 - options.config.enable_battor_trace = ( - os.environ.get('BUILDBOT_MASTERNAME') == 'chromium.perf.fyi') + options.config.enable_battor_trace = True options.config.enable_chrome_trace = False options.config.chrome_trace_config.SetDefaultOverheadFilter() options.SetTimelineBasedMetrics(['powerMetric', 'clockSyncLatencyMetric'])
diff --git a/tools/perf/benchmarks/system_health.py b/tools/perf/benchmarks/system_health.py index 4cbd8a9a..6303fbb 100644 --- a/tools/perf/benchmarks/system_health.py +++ b/tools/perf/benchmarks/system_health.py
@@ -2,7 +2,6 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import os import re from core import perf_benchmark @@ -34,11 +33,7 @@ options = timeline_based_measurement.Options( chrome_trace_category_filter.ChromeTraceCategoryFilter()) options.config.chrome_trace_config.category_filter.AddFilterString('rail') - # TODO(charliea): Reenable BattOr tracing on the main perf waterfall once - # the BattOrs stop crashing as their SD cards fill up. - # crbug.com/652384 - options.config.enable_battor_trace = ( - os.environ.get('BUILDBOT_MASTERNAME') == 'chromium.perf.fyi') + options.config.enable_battor_trace = True options.config.enable_chrome_trace = True options.SetTimelineBasedMetrics(['clockSyncLatencyMetric', 'powerMetric']) return options
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc index ed9f8cc80..e73d9a4 100644 --- a/ui/accessibility/platform/ax_platform_node_win.cc +++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -899,9 +899,10 @@ COM_OBJECT_VALIDATE_1_ARG(object); if (riid == IID_IAccessible2) { - FOR_EACH_OBSERVER(IAccessible2UsageObserver, - GetIAccessible2UsageObserverList(), - OnIAccessible2Used()); + for (IAccessible2UsageObserver& observer : + GetIAccessible2UsageObserverList()) { + observer.OnIAccessible2Used(); + } } if (guidService == IID_IAccessible ||
diff --git a/ui/android/window_android.cc b/ui/android/window_android.cc index 67c6cc1..7e40c8f 100644 --- a/ui/android/window_android.cc +++ b/ui/android/window_android.cc
@@ -55,9 +55,8 @@ } void WindowAndroid::OnCompositingDidCommit() { - FOR_EACH_OBSERVER(WindowAndroidObserver, - observer_list_, - OnCompositingDidCommit()); + for (WindowAndroidObserver& observer : observer_list_) + observer.OnCompositingDidCommit(); } void WindowAndroid::AddObserver(WindowAndroidObserver* observer) { @@ -74,16 +73,14 @@ DetachCompositor(); compositor_ = compositor; - FOR_EACH_OBSERVER(WindowAndroidObserver, - observer_list_, - OnAttachCompositor()); + for (WindowAndroidObserver& observer : observer_list_) + observer.OnAttachCompositor(); } void WindowAndroid::DetachCompositor() { compositor_ = NULL; - FOR_EACH_OBSERVER(WindowAndroidObserver, - observer_list_, - OnDetachCompositor()); + for (WindowAndroidObserver& observer : observer_list_) + observer.OnDetachCompositor(); observer_list_.Clear(); } @@ -98,8 +95,8 @@ } void WindowAndroid::Animate(base::TimeTicks begin_frame_time) { - FOR_EACH_OBSERVER( - WindowAndroidObserver, observer_list_, OnAnimate(begin_frame_time)); + for (WindowAndroidObserver& observer : observer_list_) + observer.OnAnimate(begin_frame_time); } void WindowAndroid::OnVSync(JNIEnv* env, @@ -109,10 +106,8 @@ base::TimeTicks frame_time(base::TimeTicks::FromInternalValue(time_micros)); base::TimeDelta vsync_period( base::TimeDelta::FromMicroseconds(period_micros)); - FOR_EACH_OBSERVER( - WindowAndroidObserver, - observer_list_, - OnVSync(frame_time, vsync_period)); + for (WindowAndroidObserver& observer : observer_list_) + observer.OnVSync(frame_time, vsync_period); if (compositor_) compositor_->OnVSync(frame_time, vsync_period); } @@ -120,18 +115,20 @@ void WindowAndroid::OnVisibilityChanged(JNIEnv* env, const JavaParamRef<jobject>& obj, bool visible) { - FOR_EACH_OBSERVER(WindowAndroidObserver, observer_list_, - OnRootWindowVisibilityChanged(visible)); + for (WindowAndroidObserver& observer : observer_list_) + observer.OnRootWindowVisibilityChanged(visible); } void WindowAndroid::OnActivityStopped(JNIEnv* env, const JavaParamRef<jobject>& obj) { - FOR_EACH_OBSERVER(WindowAndroidObserver, observer_list_, OnActivityStopped()); + for (WindowAndroidObserver& observer : observer_list_) + observer.OnActivityStopped(); } void WindowAndroid::OnActivityStarted(JNIEnv* env, const JavaParamRef<jobject>& obj) { - FOR_EACH_OBSERVER(WindowAndroidObserver, observer_list_, OnActivityStarted()); + for (WindowAndroidObserver& observer : observer_list_) + observer.OnActivityStarted(); } bool WindowAndroid::HasPermission(const std::string& permission) {
diff --git a/ui/aura/env.cc b/ui/aura/env.cc index 058d94a..07c1138 100644 --- a/ui/aura/env.cc +++ b/ui/aura/env.cc
@@ -36,7 +36,8 @@ // Env, public: Env::~Env() { - FOR_EACH_OBSERVER(EnvObserver, observers_, OnWillDestroyEnv()); + for (EnvObserver& observer : observers_) + observer.OnWillDestroyEnv(); DCHECK_EQ(this, lazy_tls_ptr.Pointer()->Get()); lazy_tls_ptr.Pointer()->Set(NULL); } @@ -101,15 +102,18 @@ } void Env::NotifyWindowInitialized(Window* window) { - FOR_EACH_OBSERVER(EnvObserver, observers_, OnWindowInitialized(window)); + for (EnvObserver& observer : observers_) + observer.OnWindowInitialized(window); } void Env::NotifyHostInitialized(WindowTreeHost* host) { - FOR_EACH_OBSERVER(EnvObserver, observers_, OnHostInitialized(host)); + for (EnvObserver& observer : observers_) + observer.OnHostInitialized(host); } void Env::NotifyHostActivated(WindowTreeHost* host) { - FOR_EACH_OBSERVER(EnvObserver, observers_, OnHostActivated(host)); + for (EnvObserver& observer : observers_) + observer.OnHostActivated(host); } ////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/aura/test/test_cursor_client.cc b/ui/aura/test/test_cursor_client.cc index 123046c..54b0e07 100644 --- a/ui/aura/test/test_cursor_client.cc +++ b/ui/aura/test/test_cursor_client.cc
@@ -33,14 +33,14 @@ void TestCursorClient::ShowCursor() { visible_ = true; - FOR_EACH_OBSERVER(aura::client::CursorClientObserver, observers_, - OnCursorVisibilityChanged(true)); + for (aura::client::CursorClientObserver& observer : observers_) + observer.OnCursorVisibilityChanged(true); } void TestCursorClient::HideCursor() { visible_ = false; - FOR_EACH_OBSERVER(aura::client::CursorClientObserver, observers_, - OnCursorVisibilityChanged(false)); + for (aura::client::CursorClientObserver& observer : observers_) + observer.OnCursorVisibilityChanged(false); } void TestCursorClient::SetCursorSet(ui::CursorSetType cursor_set) {
diff --git a/ui/aura/test/test_focus_client.cc b/ui/aura/test/test_focus_client.cc index 662f9b1..027afe6 100644 --- a/ui/aura/test/test_focus_client.cc +++ b/ui/aura/test/test_focus_client.cc
@@ -44,9 +44,8 @@ if (focused_window_) observer_manager_.Add(focused_window_); - FOR_EACH_OBSERVER(aura::client::FocusChangeObserver, - focus_observers_, - OnWindowFocused(focused_window_, lost_focus)); + for (aura::client::FocusChangeObserver& observer : focus_observers_) + observer.OnWindowFocused(focused_window_, lost_focus); client::FocusChangeObserver* observer = client::GetFocusChangeObserver(old_focused_window); if (observer)
diff --git a/ui/aura/window.cc b/ui/aura/window.cc index ead05dd..be94111 100644 --- a/ui/aura/window.cc +++ b/ui/aura/window.cc
@@ -109,7 +109,8 @@ // Let the delegate know we're in the processing of destroying. if (delegate_) delegate_->OnWindowDestroying(this); - FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDestroying(this)); + for (WindowObserver& observer : observers_) + observer.OnWindowDestroying(this); // While we are being destroyed, our target handler may also be in the // process of destruction or already destroyed, so do not forward any @@ -193,9 +194,8 @@ if (title == title_) return; title_ = title; - FOR_EACH_OBSERVER(WindowObserver, - observers_, - OnWindowTitleChanged(this)); + for (WindowObserver& observer : observers_) + observer.OnWindowTitleChanged(this); } void Window::SetTransparent(bool transparent) { @@ -276,11 +276,11 @@ } void Window::SetTransform(const gfx::Transform& transform) { - FOR_EACH_OBSERVER(WindowObserver, observers_, - OnWindowTransforming(this)); + for (WindowObserver& observer : observers_) + observer.OnWindowTransforming(this); layer()->SetTransform(transform); - FOR_EACH_OBSERVER(WindowObserver, observers_, - OnWindowTransformed(this)); + for (WindowObserver& observer : observers_) + observer.OnWindowTransformed(this); NotifyAncestorWindowTransformed(this); } @@ -384,7 +384,8 @@ children_.push_back(child); if (layout_manager_) layout_manager_->OnWindowAddedToLayout(child); - FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowAdded(child)); + for (WindowObserver& observer : observers_) + observer.OnWindowAdded(child); child->OnParentChanged(); Window* root_window = GetRootWindow(); @@ -677,8 +678,8 @@ prop_value.deallocator = deallocator; prop_map_[key] = prop_value; } - FOR_EACH_OBSERVER(WindowObserver, observers_, - OnWindowPropertyChanged(this, key, old)); + for (WindowObserver& observer : observers_) + observer.OnWindowPropertyChanged(this, key, old); return old; } @@ -724,8 +725,8 @@ if (visible == layer()->GetTargetVisibility()) return; // No change. - FOR_EACH_OBSERVER(WindowObserver, observers_, - OnWindowVisibilityChanging(this, visible)); + for (WindowObserver& observer : observers_) + observer.OnWindowVisibilityChanging(this, visible); client::VisibilityClient* visibility_client = client::GetVisibilityClient(this); @@ -811,7 +812,8 @@ void Window::RemoveChildImpl(Window* child, Window* new_parent) { if (layout_manager_) layout_manager_->OnWillRemoveWindowFromLayout(child); - FOR_EACH_OBSERVER(WindowObserver, observers_, OnWillRemoveWindow(child)); + for (WindowObserver& observer : observers_) + observer.OnWillRemoveWindow(child); Window* root_window = child->GetRootWindow(); Window* new_root_window = new_parent ? new_parent->GetRootWindow() : NULL; if (root_window && root_window != new_root_window) @@ -829,8 +831,8 @@ } void Window::OnParentChanged() { - FOR_EACH_OBSERVER( - WindowObserver, observers_, OnWindowParentChanged(this, parent_)); + for (WindowObserver& observer : observers_) + observer.OnWindowParentChanged(this, parent_); } void Window::StackChildRelativeTo(Window* child, @@ -881,12 +883,13 @@ } void Window::OnStackingChanged() { - FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowStackingChanged(this)); + for (WindowObserver& observer : observers_) + observer.OnWindowStackingChanged(this); } void Window::NotifyRemovingFromRootWindow(Window* new_root) { - FOR_EACH_OBSERVER(WindowObserver, observers_, - OnWindowRemovingFromRootWindow(this, new_root)); + for (WindowObserver& observer : observers_) + observer.OnWindowRemovingFromRootWindow(this, new_root); for (Window::Windows::const_iterator it = children_.begin(); it != children_.end(); ++it) { (*it)->NotifyRemovingFromRootWindow(new_root); @@ -894,8 +897,8 @@ } void Window::NotifyAddedToRootWindow() { - FOR_EACH_OBSERVER(WindowObserver, observers_, - OnWindowAddedToRootWindow(this)); + for (WindowObserver& observer : observers_) + observer.OnWindowAddedToRootWindow(this); for (Window::Windows::const_iterator it = children_.begin(); it != children_.end(); ++it) { (*it)->NotifyAddedToRootWindow(); @@ -963,8 +966,8 @@ // exit without further access to any members. WindowTracker tracker; tracker.Add(this); - FOR_EACH_OBSERVER(WindowObserver, observers_, - OnWindowVisibilityChanged(target, visible)); + for (WindowObserver& observer : observers_) + observer.OnWindowVisibilityChanged(target, visible); return tracker.Contains(this); } @@ -1002,8 +1005,8 @@ } void Window::NotifyAncestorWindowTransformed(Window* source) { - FOR_EACH_OBSERVER(WindowObserver, observers_, - OnAncestorWindowTransformed(source, this)); + for (WindowObserver& observer : observers_) + observer.OnAncestorWindowTransformed(source, this); for (Window::Windows::const_iterator it = children_.begin(); it != children_.end(); ++it) { (*it)->NotifyAncestorWindowTransformed(source); @@ -1029,9 +1032,8 @@ void Window::OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) { DCHECK(layer()); - FOR_EACH_OBSERVER(WindowObserver, - observers_, - OnDelegatedFrameDamage(this, damage_rect_in_dip)); + for (WindowObserver& observer : observers_) + observer.OnDelegatedFrameDamage(this, damage_rect_in_dip); } void Window::OnLayerBoundsChanged(const gfx::Rect& old_bounds) {
diff --git a/ui/aura/window_tree_host.cc b/ui/aura/window_tree_host.cc index afa08aa..2da90f8 100644 --- a/ui/aura/window_tree_host.cc +++ b/ui/aura/window_tree_host.cc
@@ -270,8 +270,8 @@ TRACE_EVENT1("ui", "WindowTreeHost::OnHostMoved", "origin", new_location.ToString()); - FOR_EACH_OBSERVER(WindowTreeHostObserver, observers_, - OnHostMoved(this, new_location)); + for (WindowTreeHostObserver& observer : observers_) + observer.OnHostMoved(this, new_location); } void WindowTreeHost::OnHostResized(const gfx::Size& new_size) { @@ -287,17 +287,18 @@ // The layer, and the observers should be notified of the // transformed size of the root window. UpdateRootWindowSize(layer_size); - FOR_EACH_OBSERVER(WindowTreeHostObserver, observers_, OnHostResized(this)); + for (WindowTreeHostObserver& observer : observers_) + observer.OnHostResized(this); } void WindowTreeHost::OnHostWorkspaceChanged() { - FOR_EACH_OBSERVER(WindowTreeHostObserver, observers_, - OnHostWorkspaceChanged(this)); + for (WindowTreeHostObserver& observer : observers_) + observer.OnHostWorkspaceChanged(this); } void WindowTreeHost::OnHostCloseRequested() { - FOR_EACH_OBSERVER(WindowTreeHostObserver, observers_, - OnHostCloseRequested(this)); + for (WindowTreeHostObserver& observer : observers_) + observer.OnHostCloseRequested(this); } void WindowTreeHost::OnHostActivated() {
diff --git a/ui/base/clipboard/clipboard_monitor.cc b/ui/base/clipboard/clipboard_monitor.cc index 8991714..984e766 100644 --- a/ui/base/clipboard/clipboard_monitor.cc +++ b/ui/base/clipboard/clipboard_monitor.cc
@@ -23,7 +23,8 @@ void ClipboardMonitor::NotifyClipboardDataChanged() { DCHECK(CalledOnValidThread()); - FOR_EACH_OBSERVER(ClipboardObserver, observers_, OnClipboardDataChanged()); + for (ClipboardObserver& observer : observers_) + observer.OnClipboardDataChanged(); } void ClipboardMonitor::AddObserver(ClipboardObserver* observer) {
diff --git a/ui/base/ime/chromeos/ime_keyboard.cc b/ui/base/ime/chromeos/ime_keyboard.cc index 581a3c5..24273f8 100644 --- a/ui/base/ime/chromeos/ime_keyboard.cc +++ b/ui/base/ime/chromeos/ime_keyboard.cc
@@ -79,8 +79,8 @@ bool old_state = caps_lock_is_enabled_; caps_lock_is_enabled_ = enable_caps_lock; if (old_state != enable_caps_lock) { - FOR_EACH_OBSERVER(ImeKeyboard::Observer, observers_, - OnCapsLockChanged(enable_caps_lock)); + for (ImeKeyboard::Observer& observer : observers_) + observer.OnCapsLockChanged(enable_caps_lock); } }
diff --git a/ui/base/ime/input_method_base.cc b/ui/base/ime/input_method_base.cc index 37c4d90a..9d43d852 100644 --- a/ui/base/ime/input_method_base.cc +++ b/ui/base/ime/input_method_base.cc
@@ -22,9 +22,8 @@ text_input_client_(nullptr) {} InputMethodBase::~InputMethodBase() { - FOR_EACH_OBSERVER(InputMethodObserver, - observer_list_, - OnInputMethodDestroyed(this)); + for (InputMethodObserver& observer : observer_list_) + observer.OnInputMethodDestroyed(this); if (ui::IMEBridge::Get() && ui::IMEBridge::Get()->GetInputContextHandler() == this) ui::IMEBridge::Get()->SetInputContextHandler(nullptr); @@ -93,7 +92,8 @@ } void InputMethodBase::ShowImeIfNeeded() { - FOR_EACH_OBSERVER(InputMethodObserver, observer_list_, OnShowImeIfNeeded()); + for (InputMethodObserver& observer : observer_list_) + observer.OnShowImeIfNeeded(); } void InputMethodBase::AddObserver(InputMethodObserver* observer) { @@ -128,15 +128,14 @@ void InputMethodBase::NotifyTextInputStateChanged( const TextInputClient* client) { - FOR_EACH_OBSERVER(InputMethodObserver, - observer_list_, - OnTextInputStateChanged(client)); + for (InputMethodObserver& observer : observer_list_) + observer.OnTextInputStateChanged(client); } void InputMethodBase::NotifyTextInputCaretBoundsChanged( const TextInputClient* client) { - FOR_EACH_OBSERVER( - InputMethodObserver, observer_list_, OnCaretBoundsChanged(client)); + for (InputMethodObserver& observer : observer_list_) + observer.OnCaretBoundsChanged(client); } void InputMethodBase::SetFocusedTextInputClientInternal(
diff --git a/ui/base/ime/mock_input_method.cc b/ui/base/ime/mock_input_method.cc index fbd8bcf8..d097005b 100644 --- a/ui/base/ime/mock_input_method.cc +++ b/ui/base/ime/mock_input_method.cc
@@ -14,8 +14,8 @@ } MockInputMethod::~MockInputMethod() { - FOR_EACH_OBSERVER(InputMethodObserver, observer_list_, - OnInputMethodDestroyed(this)); + for (InputMethodObserver& observer : observer_list_) + observer.OnInputMethodDestroyed(this); } void MockInputMethod::SetDelegate(internal::InputMethodDelegate* delegate) { @@ -45,11 +45,13 @@ } void MockInputMethod::OnFocus() { - FOR_EACH_OBSERVER(InputMethodObserver, observer_list_, OnFocus()); + for (InputMethodObserver& observer : observer_list_) + observer.OnFocus(); } void MockInputMethod::OnBlur() { - FOR_EACH_OBSERVER(InputMethodObserver, observer_list_, OnBlur()); + for (InputMethodObserver& observer : observer_list_) + observer.OnBlur(); } bool MockInputMethod::OnUntranslatedIMEMessage(const base::NativeEvent& event, @@ -60,18 +62,15 @@ } void MockInputMethod::OnTextInputTypeChanged(const TextInputClient* client) { - FOR_EACH_OBSERVER(InputMethodObserver, - observer_list_, - OnTextInputTypeChanged(client)); - FOR_EACH_OBSERVER(InputMethodObserver, - observer_list_, - OnTextInputStateChanged(client)); + for (InputMethodObserver& observer : observer_list_) + observer.OnTextInputTypeChanged(client); + for (InputMethodObserver& observer : observer_list_) + observer.OnTextInputStateChanged(client); } void MockInputMethod::OnCaretBoundsChanged(const TextInputClient* client) { - FOR_EACH_OBSERVER(InputMethodObserver, - observer_list_, - OnCaretBoundsChanged(client)); + for (InputMethodObserver& observer : observer_list_) + observer.OnCaretBoundsChanged(client); } void MockInputMethod::CancelComposition(const TextInputClient* client) { @@ -105,7 +104,8 @@ } void MockInputMethod::ShowImeIfNeeded() { - FOR_EACH_OBSERVER(InputMethodObserver, observer_list_, OnShowImeIfNeeded()); + for (InputMethodObserver& observer : observer_list_) + observer.OnShowImeIfNeeded(); } void MockInputMethod::AddObserver(InputMethodObserver* observer) {
diff --git a/ui/base/user_activity/user_activity_detector.cc b/ui/base/user_activity/user_activity_detector.cc index 29dec4e8..bfda1cfd 100644 --- a/ui/base/user_activity/user_activity_detector.cc +++ b/ui/base/user_activity/user_activity_detector.cc
@@ -123,7 +123,8 @@ kNotifyIntervalMs) { if (VLOG_IS_ON(1)) VLOG(1) << "Reporting user activity: " << GetEventDebugString(event); - FOR_EACH_OBSERVER(UserActivityObserver, observers_, OnUserActivity(event)); + for (UserActivityObserver& observer : observers_) + observer.OnUserActivity(event); last_observer_notification_time_ = now; } }
diff --git a/ui/base/win/osk_display_manager.cc b/ui/base/win/osk_display_manager.cc index c3abb9fc..0373b48 100644 --- a/ui/base/win/osk_display_manager.cc +++ b/ui/base/win/osk_display_manager.cc
@@ -219,8 +219,8 @@ DCHECK(!osk_visible_notification_received_); osk_visible_notification_received_ = true; - FOR_EACH_OBSERVER(OnScreenKeyboardObserver, observers_, - OnKeyboardVisible(osk_rect_pixels_)); + for (OnScreenKeyboardObserver& observer : observers_) + observer.OnKeyboardVisible(osk_rect_pixels_); // Now that the keyboard is visible, run the task to detect if it was hidden. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( @@ -231,8 +231,8 @@ void OnScreenKeyboardDetector::HandleKeyboardHidden() { osk_visible_notification_received_ = false; - FOR_EACH_OBSERVER(OnScreenKeyboardObserver, observers_, - OnKeyboardHidden(osk_rect_pixels_)); + for (OnScreenKeyboardObserver& observer : observers_) + observer.OnKeyboardHidden(osk_rect_pixels_); ClearObservers(); }
diff --git a/ui/chromeos/ime/candidate_window_view.cc b/ui/chromeos/ime/candidate_window_view.cc index 5c42421..dcfd3589 100644 --- a/ui/chromeos/ime/candidate_window_view.cc +++ b/ui/chromeos/ime/candidate_window_view.cc
@@ -409,7 +409,8 @@ const ui::Event& event) { for (size_t i = 0; i < candidate_views_.size(); ++i) { if (sender == candidate_views_[i]) { - FOR_EACH_OBSERVER(Observer, observers_, OnCandidateCommitted(i)); + for (Observer& observer : observers_) + observer.OnCandidateCommitted(i); return; } }
diff --git a/ui/chromeos/ime/input_method_menu_manager.cc b/ui/chromeos/ime/input_method_menu_manager.cc index f4032f8..08c053f 100644 --- a/ui/chromeos/ime/input_method_menu_manager.cc +++ b/ui/chromeos/ime/input_method_menu_manager.cc
@@ -35,9 +35,8 @@ void InputMethodMenuManager::SetCurrentInputMethodMenuItemList( const InputMethodMenuItemList& menu_list) { menu_list_ = menu_list; - FOR_EACH_OBSERVER(InputMethodMenuManager::Observer, - observers_, - InputMethodMenuItemChanged(this)); + for (InputMethodMenuManager::Observer& observer : observers_) + observer.InputMethodMenuItemChanged(this); } bool InputMethodMenuManager::HasInputMethodMenuItemForKey(
diff --git a/ui/chromeos/network/network_icon_animation.cc b/ui/chromeos/network/network_icon_animation.cc index 055a10b0..9ed3a47 100644 --- a/ui/chromeos/network/network_icon_animation.cc +++ b/ui/chromeos/network/network_icon_animation.cc
@@ -27,7 +27,8 @@ const gfx::Animation* animation) { if (animation != &animation_) return; - FOR_EACH_OBSERVER(AnimationObserver, observers_, NetworkIconChanged()); + for (AnimationObserver& observer : observers_) + observer.NetworkIconChanged(); } double NetworkIconAnimation::GetAnimation() {
diff --git a/ui/display/chromeos/display_configurator.cc b/ui/display/chromeos/display_configurator.cc index b0bd0a0..6c30623 100644 --- a/ui/display/chromeos/display_configurator.cc +++ b/ui/display/chromeos/display_configurator.cc
@@ -1109,18 +1109,17 @@ bool success, MultipleDisplayState attempted_state) { if (success) { - FOR_EACH_OBSERVER( - Observer, observers_, OnDisplayModeChanged(cached_displays_)); + for (Observer& observer : observers_) + observer.OnDisplayModeChanged(cached_displays_); } else { - FOR_EACH_OBSERVER( - Observer, observers_, OnDisplayModeChangeFailed(cached_displays_, - attempted_state)); + for (Observer& observer : observers_) + observer.OnDisplayModeChangeFailed(cached_displays_, attempted_state); } } void DisplayConfigurator::NotifyPowerStateObservers() { - FOR_EACH_OBSERVER( - Observer, observers_, OnPowerStateChanged(current_power_state_)); + for (Observer& observer : observers_) + observer.OnPowerStateChanged(current_power_state_); } int64_t DisplayConfigurator::AddVirtualDisplay(const gfx::Size& display_size) {
diff --git a/ui/display/chromeos/x11/native_display_delegate_x11.cc b/ui/display/chromeos/x11/native_display_delegate_x11.cc index 7fe27ffc5..3cbfe69 100644 --- a/ui/display/chromeos/x11/native_display_delegate_x11.cc +++ b/ui/display/chromeos/x11/native_display_delegate_x11.cc
@@ -90,8 +90,8 @@ return delegate_->cached_outputs_.get(); } void NotifyDisplayObservers() override { - FOR_EACH_OBSERVER( - NativeDisplayObserver, delegate_->observers_, OnConfigurationChanged()); + for (NativeDisplayObserver& observer : delegate_->observers_) + observer.OnConfigurationChanged(); } private:
diff --git a/ui/display/display_change_notifier.cc b/ui/display/display_change_notifier.cc index 9410ca7..626c86ee 100644 --- a/ui/display/display_change_notifier.cc +++ b/ui/display/display_change_notifier.cc
@@ -48,8 +48,8 @@ for (; old_it != old_displays.end(); ++old_it) { if (std::find_if(new_displays.begin(), new_displays.end(), DisplayComparator(*old_it)) == new_displays.end()) { - FOR_EACH_OBSERVER(DisplayObserver, observer_list_, - OnDisplayRemoved(*old_it)); + for (DisplayObserver& observer : observer_list_) + observer.OnDisplayRemoved(*old_it); } } @@ -61,8 +61,8 @@ old_displays.begin(), old_displays.end(), DisplayComparator(*new_it)); if (old_it == old_displays.end()) { - FOR_EACH_OBSERVER(DisplayObserver, observer_list_, - OnDisplayAdded(*new_it)); + for (DisplayObserver& observer : observer_list_) + observer.OnDisplayAdded(*new_it); continue; } @@ -81,8 +81,8 @@ metrics |= DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR; if (metrics != DisplayObserver::DISPLAY_METRIC_NONE) { - FOR_EACH_OBSERVER(DisplayObserver, observer_list_, - OnDisplayMetricsChanged(*new_it, metrics)); + for (DisplayObserver& observer : observer_list_) + observer.OnDisplayMetricsChanged(*new_it, metrics); } } }
diff --git a/ui/display/display_list.cc b/ui/display/display_list.cc index 842808a..6aea752 100644 --- a/ui/display/display_list.cc +++ b/ui/display/display_list.cc
@@ -92,8 +92,8 @@ display::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR; } if (should_notify_observers()) { - FOR_EACH_OBSERVER(display::DisplayObserver, observers_, - OnDisplayMetricsChanged(*local_display, changed_values)); + for (display::DisplayObserver& observer : observers_) + observer.OnDisplayMetricsChanged(*local_display, changed_values); } } @@ -103,8 +103,8 @@ if (type == Type::PRIMARY) primary_display_index_ = static_cast<int>(displays_.size()) - 1; if (should_notify_observers()) { - FOR_EACH_OBSERVER(display::DisplayObserver, observers_, - OnDisplayAdded(display)); + for (display::DisplayObserver& observer : observers_) + observer.OnDisplayAdded(display); } } @@ -123,8 +123,8 @@ const display::Display display = *iter; displays_.erase(iter); if (should_notify_observers()) { - FOR_EACH_OBSERVER(display::DisplayObserver, observers_, - OnDisplayRemoved(display)); + for (display::DisplayObserver& observer : observers_) + observer.OnDisplayRemoved(display); } }
diff --git a/ui/display/fake_display_delegate.cc b/ui/display/fake_display_delegate.cc index ccfc64d..2f1503d 100644 --- a/ui/display/fake_display_delegate.cc +++ b/ui/display/fake_display_delegate.cc
@@ -228,8 +228,8 @@ if (!initialized_) return; - FOR_EACH_OBSERVER(ui::NativeDisplayObserver, observers_, - OnConfigurationChanged()); + for (ui::NativeDisplayObserver& observer : observers_) + observer.OnConfigurationChanged(); } } // namespace display
diff --git a/ui/events/devices/device_data_manager.cc b/ui/events/devices/device_data_manager.cc index b54cd56..94bbeb1 100644 --- a/ui/events/devices/device_data_manager.cc +++ b/ui/events/devices/device_data_manager.cc
@@ -12,9 +12,10 @@ #include "ui/gfx/geometry/point3_f.h" // This macro provides the implementation for the observer notification methods. -#define NOTIFY_OBSERVERS(method_decl, observer_call) \ - void DeviceDataManager::method_decl { \ - FOR_EACH_OBSERVER(InputDeviceEventObserver, observers_, observer_call); \ +#define NOTIFY_OBSERVERS(method_decl, observer_call) \ + void DeviceDataManager::method_decl { \ + for (InputDeviceEventObserver& observer : observers_) \ + observer.observer_call; \ } namespace ui {
diff --git a/ui/events/ozone/device/device_manager_manual.cc b/ui/events/ozone/device/device_manager_manual.cc index 65d1c253..aabb0a7 100644 --- a/ui/events/ozone/device/device_manager_manual.cc +++ b/ui/events/ozone/device/device_manager_manual.cc
@@ -66,7 +66,8 @@ for (; it != result->end(); ++it) { devices_.push_back(*it); DeviceEvent event(DeviceEvent::INPUT, DeviceEvent::ADD, *it); - FOR_EACH_OBSERVER(DeviceEventObserver, observers_, OnDeviceEvent(event)); + for (DeviceEventObserver& observer : observers_) + observer.OnDeviceEvent(event); } }
diff --git a/ui/events/ozone/device/udev/device_manager_udev.cc b/ui/events/ozone/device/udev/device_manager_udev.cc index 47776dac..2f8ee26 100644 --- a/ui/events/ozone/device/udev/device_manager_udev.cc +++ b/ui/events/ozone/device/udev/device_manager_udev.cc
@@ -148,8 +148,8 @@ std::unique_ptr<DeviceEvent> event = ProcessMessage(device.get()); if (event) - FOR_EACH_OBSERVER( - DeviceEventObserver, observers_, OnDeviceEvent(*event.get())); + for (DeviceEventObserver& observer : observers_) + observer.OnDeviceEvent(*event.get()); } void DeviceManagerUdev::OnFileCanWriteWithoutBlocking(int fd) {
diff --git a/ui/events/platform/platform_event_source.cc b/ui/events/platform/platform_event_source.cc index dfab7fc..9be6630e 100644 --- a/ui/events/platform/platform_event_source.cc +++ b/ui/events/platform/platform_event_source.cc
@@ -69,8 +69,8 @@ uint32_t PlatformEventSource::DispatchEvent(PlatformEvent platform_event) { uint32_t action = POST_DISPATCH_PERFORM_DEFAULT; - FOR_EACH_OBSERVER(PlatformEventObserver, observers_, - WillProcessEvent(platform_event)); + for (PlatformEventObserver& observer : observers_) + observer.WillProcessEvent(platform_event); // Give the overridden dispatcher a chance to dispatch the event first. if (overridden_dispatcher_) action = overridden_dispatcher_->DispatchEvent(platform_event); @@ -83,8 +83,8 @@ break; } } - FOR_EACH_OBSERVER(PlatformEventObserver, observers_, - DidProcessEvent(platform_event)); + for (PlatformEventObserver& observer : observers_) + observer.DidProcessEvent(platform_event); // If an overridden dispatcher has been destroyed, then the platform // event-source should halt dispatching the current stream of events, and wait
diff --git a/ui/gfx/sys_color_change_listener.cc b/ui/gfx/sys_color_change_listener.cc index 8c6cd04e7..0c1f7928 100644 --- a/ui/gfx/sys_color_change_listener.cc +++ b/ui/gfx/sys_color_change_listener.cc
@@ -94,7 +94,8 @@ if (message == WM_SYSCOLORCHANGE || (message == WM_SETTINGCHANGE && wparam == SPI_SETHIGHCONTRAST)) { UpdateInvertedColorScheme(); - FOR_EACH_OBSERVER(SysColorChangeListener, listeners_, OnSysColorChange()); + for (SysColorChangeListener& observer : listeners_) + observer.OnSysColorChange(); } }
diff --git a/ui/gfx/win/singleton_hwnd.cc b/ui/gfx/win/singleton_hwnd.cc index 8142187..7d3aa6e 100644 --- a/ui/gfx/win/singleton_hwnd.cc +++ b/ui/gfx/win/singleton_hwnd.cc
@@ -21,9 +21,8 @@ LPARAM lparam, LRESULT& result, DWORD msg_map_id) { - FOR_EACH_OBSERVER(SingletonHwndObserver, - observer_list_, - OnWndProc(window, message, wparam, lparam)); + for (SingletonHwndObserver& observer : observer_list_) + observer.OnWndProc(window, message, wparam, lparam); return false; } @@ -42,7 +41,8 @@ DestroyWindow(hwnd()); // Tell all of our current observers to clean themselves up. - FOR_EACH_OBSERVER(SingletonHwndObserver, observer_list_, ClearWndProc()); + for (SingletonHwndObserver& observer : observer_list_) + observer.ClearWndProc(); } void SingletonHwnd::AddObserver(SingletonHwndObserver* observer) {
diff --git a/ui/gl/gpu_switching_manager.cc b/ui/gl/gpu_switching_manager.cc index 5fdb3cc..9718114 100644 --- a/ui/gl/gpu_switching_manager.cc +++ b/ui/gl/gpu_switching_manager.cc
@@ -128,7 +128,8 @@ } void GpuSwitchingManager::NotifyGpuSwitched() { - FOR_EACH_OBSERVER(GpuSwitchingObserver, observer_list_, OnGpuSwitched()); + for (GpuSwitchingObserver& observer : observer_list_) + observer.OnGpuSwitched(); } gl::GpuPreference GpuSwitchingManager::AdjustGpuPreference(
diff --git a/ui/keyboard/keyboard_controller.cc b/ui/keyboard/keyboard_controller.cc index 35eea587..de8bcbe 100644 --- a/ui/keyboard/keyboard_controller.cc +++ b/ui/keyboard/keyboard_controller.cc
@@ -188,8 +188,8 @@ } if (input_method_) input_method_->RemoveObserver(this); - FOR_EACH_OBSERVER(KeyboardControllerObserver, observer_list_, - OnKeyboardClosed()); + for (KeyboardControllerObserver& observer : observer_list_) + observer.OnKeyboardClosed(); ui_->SetController(nullptr); } @@ -221,9 +221,8 @@ const gfx::Rect& new_bounds) { current_keyboard_bounds_ = new_bounds; if (ui_->HasKeyboardWindow() && ui_->GetKeyboardWindow()->IsVisible()) { - FOR_EACH_OBSERVER(KeyboardControllerObserver, - observer_list_, - OnKeyboardBoundsChanging(new_bounds)); + for (KeyboardControllerObserver& observer : observer_list_) + observer.OnKeyboardBoundsChanging(new_bounds); if (keyboard::IsKeyboardOverscrollEnabled()) ui_->InitInsets(new_bounds); else @@ -488,8 +487,8 @@ void KeyboardController::HideAnimationFinished() { ui_->HideKeyboardContainer(container_.get()); - FOR_EACH_OBSERVER(KeyboardControllerObserver, observer_list_, - OnKeyboardHidden()); + for (KeyboardControllerObserver& observer : observer_list_) + observer.OnKeyboardHidden(); } } // namespace keyboard
diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc index e4911c9..9661318 100644 --- a/ui/native_theme/native_theme.cc +++ b/ui/native_theme/native_theme.cc
@@ -35,8 +35,8 @@ } void NativeTheme::NotifyObservers() { - FOR_EACH_OBSERVER(NativeThemeObserver, native_theme_observers_, - OnNativeThemeUpdated(this)); + for (NativeThemeObserver& observer : native_theme_observers_) + observer.OnNativeThemeUpdated(this); } NativeTheme::NativeTheme()
diff --git a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc index 8d619eb..75096a0 100644 --- a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc +++ b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc
@@ -113,8 +113,8 @@ send_runner_ = send_runner; send_callback_ = send_callback; - FOR_EACH_OBSERVER(GpuThreadObserver, gpu_thread_observers_, - OnGpuThreadReady()); + for (GpuThreadObserver& observer : gpu_thread_observers_) + observer.OnGpuThreadReady(); // The cursor is special since it will process input events on the IO thread // and can by-pass the UI thread. This means that we need to special case it @@ -133,8 +133,8 @@ host_id_ = -1; send_runner_ = nullptr; send_callback_.Reset(); - FOR_EACH_OBSERVER(GpuThreadObserver, gpu_thread_observers_, - OnGpuThreadRetired()); + for (GpuThreadObserver& observer : gpu_thread_observers_) + observer.OnGpuThreadRetired(); } }
diff --git a/ui/ozone/platform/drm/host/drm_native_display_delegate.cc b/ui/ozone/platform/drm/host/drm_native_display_delegate.cc index 45fbc84..0a16a7e 100644 --- a/ui/ozone/platform/drm/host/drm_native_display_delegate.cc +++ b/ui/ozone/platform/drm/host/drm_native_display_delegate.cc
@@ -21,8 +21,8 @@ } void DrmNativeDisplayDelegate::OnConfigurationChanged() { - FOR_EACH_OBSERVER(NativeDisplayObserver, observers_, - OnConfigurationChanged()); + for (NativeDisplayObserver& observer : observers_) + observer.OnConfigurationChanged(); } void DrmNativeDisplayDelegate::Initialize() {
diff --git a/ui/ozone/platform/drm/mus_thread_proxy.cc b/ui/ozone/platform/drm/mus_thread_proxy.cc index dd1c5d74..c70850a 100644 --- a/ui/ozone/platform/drm/mus_thread_proxy.cc +++ b/ui/ozone/platform/drm/mus_thread_proxy.cc
@@ -42,8 +42,8 @@ MusThreadProxy::~MusThreadProxy() { DCHECK(on_window_server_thread_.CalledOnValidThread()); - FOR_EACH_OBSERVER(GpuThreadObserver, gpu_thread_observers_, - OnGpuThreadRetired()); + for (GpuThreadObserver& observer : gpu_thread_observers_) + observer.OnGpuThreadRetired(); } // This is configured on the GPU thread. @@ -74,8 +74,8 @@ void MusThreadProxy::RunObservers() { DCHECK(on_window_server_thread_.CalledOnValidThread()); - FOR_EACH_OBSERVER(GpuThreadObserver, gpu_thread_observers_, - OnGpuThreadReady()); + for (GpuThreadObserver& observer : gpu_thread_observers_) + observer.OnGpuThreadReady(); } void MusThreadProxy::AddGpuThreadObserver(GpuThreadObserver* observer) {
diff --git a/ui/snapshot/screenshot_grabber.cc b/ui/snapshot/screenshot_grabber.cc index ebccaae..7b35748a 100644 --- a/ui/snapshot/screenshot_grabber.cc +++ b/ui/snapshot/screenshot_grabber.cc
@@ -187,8 +187,8 @@ #if defined(USE_AURA) cursor_hider_.reset(); #endif - FOR_EACH_OBSERVER(ScreenshotGrabberObserver, observers_, - OnScreenshotCompleted(screenshot_result, screenshot_path)); + for (ScreenshotGrabberObserver& observer : observers_) + observer.OnScreenshotCompleted(screenshot_result, screenshot_path); } void ScreenshotGrabber::AddObserver(ScreenshotGrabberObserver* observer) {
diff --git a/ui/views/controls/scroll_view.cc b/ui/views/controls/scroll_view.cc index a358128..58fa126 100644 --- a/ui/views/controls/scroll_view.cc +++ b/ui/views/controls/scroll_view.cc
@@ -183,12 +183,7 @@ if (!base::FeatureList::IsEnabled(kToolkitViewsScrollWithLayers)) return; - - background_color_ = SK_ColorWHITE; - contents_viewport_->set_background( - Background::CreateSolidBackground(background_color_)); - contents_viewport_->SetPaintToLayer(true); - contents_viewport_->layer()->SetMasksToBounds(true); + EnableViewPortLayer(); } ScrollView::~ScrollView() { @@ -699,6 +694,14 @@ return contents_viewport_->layer() != nullptr; } +void ScrollView::EnableViewPortLayer() { + background_color_ = SK_ColorWHITE; + contents_viewport_->set_background( + Background::CreateSolidBackground(background_color_)); + contents_viewport_->SetPaintToLayer(true); + contents_viewport_->layer()->SetMasksToBounds(true); +} + void ScrollView::OnLayerScrolled() { UpdateScrollBarPositions(); ScrollHeader();
diff --git a/ui/views/controls/scroll_view.h b/ui/views/controls/scroll_view.h index ff444bb..8e30b169 100644 --- a/ui/views/controls/scroll_view.h +++ b/ui/views/controls/scroll_view.h
@@ -109,6 +109,10 @@ bool is_page, bool is_positive) override; + // TODO(djacobo): Remove this method when http://crbug.com/656198 is closed. + // Force |contents_viewport_| to enable a Layer(). + void EnableViewPortLayer(); + private: friend class test::ScrollViewTestApi;