blob: 11329e936f8479f3ed68bd990bef8fd922757c7c [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/ime/ime_window.h"
#include <utility>
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/ime/ime_native_window.h"
#include "chrome/browser/ui/ime/ime_window_observer.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest_handlers/icons_handler.h"
#include "ui/display/display.h"
#include "ui/display/display_finder.h"
#include "ui/display/screen.h"
#include "ui/gfx/image/image.h"
namespace {
// The vertical margin between the cursor and the follow-cursor window.
const int kFollowCursorMargin = 3;
// The offset from the left of follow cursor window to the left of cursor.
const int kFollowCursorOffset = 32;
} // namespace
namespace ui {
ImeWindow::ImeWindow(Profile* profile,
const extensions::Extension* extension,
content::RenderFrameHost* opener_render_frame_host,
const std::string& url,
Mode mode,
const gfx::Rect& bounds)
: mode_(mode), native_window_(nullptr) {
if (extension) { // Allow nullable |extension| for testability.
title_ = extension->name();
icon_.reset(new extensions::IconImage(
profile, extension, extensions::IconsInfo::GetIcons(extension),
extension_misc::EXTENSION_ICON_BITTY, gfx::ImageSkia(), this));
}
registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
content::NotificationService::AllSources());
GURL gurl(url);
if (!gurl.is_valid())
gurl = extension->GetResourceURL(url);
scoped_refptr<content::SiteInstance> site_instance =
opener_render_frame_host ? opener_render_frame_host->GetSiteInstance()
: nullptr;
if (!site_instance ||
site_instance->GetSiteURL().GetOrigin() != gurl.GetOrigin()) {
site_instance = content::SiteInstance::CreateForURL(profile, gurl);
}
content::WebContents::CreateParams create_params(profile,
std::move(site_instance));
if (opener_render_frame_host) {
create_params.opener_render_process_id =
opener_render_frame_host->GetProcess()->GetID();
create_params.opener_render_frame_id =
opener_render_frame_host->GetRoutingID();
}
web_contents_ = content::WebContents::Create(create_params);
web_contents_->SetDelegate(this);
content::OpenURLParams params(gurl, content::Referrer(),
WindowOpenDisposition::SINGLETON_TAB,
ui::PAGE_TRANSITION_LINK, false);
web_contents_->OpenURL(params);
native_window_ = CreateNativeWindow(this, bounds, web_contents_.get());
}
void ImeWindow::Show() {
native_window_->Show();
}
void ImeWindow::Hide() {
native_window_->Hide();
}
void ImeWindow::Close() {
web_contents_.reset();
native_window_->Close();
}
void ImeWindow::SetBounds(const gfx::Rect& bounds) {
native_window_->SetBounds(bounds);
}
void ImeWindow::FollowCursor(const gfx::Rect& cursor_bounds) {
if (mode_ != FOLLOW_CURSOR)
return;
gfx::Rect screen_bounds =
display::FindDisplayNearestPoint(
display::Screen::GetScreen()->GetAllDisplays(),
gfx::Point(cursor_bounds.x(), cursor_bounds.y()))->bounds();
gfx::Rect window_bounds = native_window_->GetBounds();
int screen_width = screen_bounds.x() + screen_bounds.width();
int screen_height = screen_bounds.y() + screen_bounds.height();
int width = window_bounds.width();
int height = window_bounds.height();
// By default, aligns the left of the window client area to the left of the
// cursor, and aligns the top of the window to the bottom of the cursor.
// If the right of the window would go beyond the screen bounds, aligns the
// right of the window to the screen bounds.
// If the bottom of the window would go beyond the screen bounds, aligns the
// bottom of the window to the cursor top.
int x = cursor_bounds.x() - kFollowCursorOffset;
int y = cursor_bounds.y() + cursor_bounds.height() + kFollowCursorMargin;
if (width < screen_width && x + width > screen_width)
x = screen_width - width;
if (height < screen_height && y + height > screen_height)
y = cursor_bounds.y() - height - kFollowCursorMargin;
window_bounds.set_x(x);
window_bounds.set_y(y);
SetBounds(window_bounds);
}
int ImeWindow::GetFrameId() const {
return web_contents_->GetMainFrame()->GetRoutingID();
}
void ImeWindow::OnWindowDestroyed() {
for (ImeWindowObserver& observer : observers_)
observer.OnWindowDestroyed(this);
native_window_ = nullptr;
delete this;
}
void ImeWindow::AddObserver(ImeWindowObserver* observer) {
observers_.AddObserver(observer);
}
void ImeWindow::RemoveObserver(ImeWindowObserver* observer) {
observers_.RemoveObserver(observer);
}
void ImeWindow::OnExtensionIconImageChanged(extensions::IconImage* image) {
if (native_window_)
native_window_->UpdateWindowIcon();
}
ImeWindow::~ImeWindow() {}
void ImeWindow::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type);
Close();
}
content::WebContents* ImeWindow::OpenURLFromTab(
content::WebContents* source,
const content::OpenURLParams& params) {
source->GetController().LoadURL(params.url, params.referrer,
params.transition, params.extra_headers);
return source;
}
bool ImeWindow::CanDragEnter(
content::WebContents* source,
const content::DropData& data,
blink::WebDragOperationsMask operations_allowed) {
return false;
}
void ImeWindow::CloseContents(content::WebContents* source) {
Close();
}
void ImeWindow::SetContentsBounds(content::WebContents* source,
const gfx::Rect& bounds) {
if (!native_window_)
return;
if (mode_ == NORMAL) {
native_window_->SetBounds(bounds);
return;
}
// Follow-cursor window needs to remain the x/y and only allow JS to
// change the size.
gfx::Rect native_bounds = native_window_->GetBounds();
native_bounds.set_width(bounds.width());
native_bounds.set_height(bounds.height());
native_window_->SetBounds(native_bounds);
}
} // namespace ui