blob: f9c7613b3da5a4bd0e19f94bb88db4c7d680b687 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/widget/unique_widget_ptr.h"
#include <utility>
namespace views {
UniqueWidgetPtr::UniqueWidgetPtr() = default;
UniqueWidgetPtr::UniqueWidgetPtr(std::unique_ptr<Widget> widget) {
Init(std::move(widget));
}
UniqueWidgetPtr::UniqueWidgetPtr(UniqueWidgetPtr&& other) = default;
UniqueWidgetPtr& UniqueWidgetPtr::operator=(UniqueWidgetPtr&& other) = default;
UniqueWidgetPtr::~UniqueWidgetPtr() = default;
UniqueWidgetPtr::operator bool() const {
return !!get();
}
Widget& UniqueWidgetPtr::operator*() const {
return *get();
}
Widget* UniqueWidgetPtr::operator->() const {
return get();
}
void UniqueWidgetPtr::reset() {
unique_widget_ptr_impl_.reset();
}
Widget* UniqueWidgetPtr::get() const {
return unique_widget_ptr_impl_ ? unique_widget_ptr_impl_->Get() : nullptr;
}
void UniqueWidgetPtr::Init(std::unique_ptr<Widget> widget) {
unique_widget_ptr_impl_ = std::make_unique<Impl>(std::move(widget));
}
void UniqueWidgetPtr::Impl::WidgetAutoCloser::operator()(Widget* widget) {
switch (widget->ownership()) {
case Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET:
// Causes the `widget`'s internal native widget to be deleted, which in
// turn deletes the `widget` itself. Cannot delete the `widget` directly
// since it's owned by the native widget in this case.
widget->CloseWithReason(Widget::ClosedReason::kUnspecified);
break;
case Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET:
case Widget::InitParams::CLIENT_OWNS_WIDGET:
// Deleting the `widget` causes the native widget in both of these
// cases to be destroyed under the hood.
delete widget;
break;
}
}
UniqueWidgetPtr::Impl::Impl() = default;
UniqueWidgetPtr::Impl::Impl(std::unique_ptr<Widget> widget)
: widget_closer_(widget.release()) {
widget_observation_.Observe(Get());
}
UniqueWidgetPtr::Impl::~Impl() = default;
Widget* UniqueWidgetPtr::Impl::Get() const {
// See cases 2 and 3 in `UniqueWidgetPtr::Impl::OnWidgetDestroying()`. In
// these cases, the `widget_closer_` may still be non-null even though the
// destruction signal has been received. In this case, the widget is alive but
// unusable, so returning `nullptr` to the caller is appropriate.
return received_widget_destruction_signal_ ? nullptr : widget_closer_.get();
}
void UniqueWidgetPtr::Impl::OnWidgetDestroying(Widget* widget) {
// Releasing `widget_closer_` here when the ownership model is
// `NATIVE_WIDGET_OWNS_WIDGET` prevents closing the widget in
// `UniqueWidgetPtr::Impl::WidgetAutoCloser::operator()` after it's
// destroyed (use-after-free).
//
// For the other ownership models, there are 3 cases that reach this point
// in the code:
// 1) The `UniqueWidgetPtr` went out of scope and the `widget` is being
// `delete`ed in `UniqueWidgetPtr::Impl::WidgetAutoCloser::operator()`.
// 2) The caller explicitly `Close()`ed the `widget`.
// 3) The `widget`'s internal native widget was destroyed somehow without any
// action from the caller.
//
// In case 1, there's no action needed and `widget_closer_` is already null
// at this point; it should not be touched. In cases 2 and 3, the `widget`
// needs to be deleted still, which will happen when the `UniqueWidgetPtr`
// goes out of scope, so the `widget_closer_` should not be released. In
// all cases, the `widget_closer_` should not be touched.
widget_observation_.Reset();
received_widget_destruction_signal_ = true;
if (widget->ownership() == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) {
DCHECK_EQ(widget, widget_closer_.get());
widget_closer_.release();
}
}
} // namespace views