// 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 "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/gfx/image/image.h" #include "ui/gfx/screen.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); 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, 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_.reset(content::WebContents::Create(create_params)); web_contents_->SetDelegate(this); content::OpenURLParams params(gurl, content::Referrer(), 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 = gfx::Screen::GetScreen()->GetPrimaryDisplay().bounds(); gfx::Rect window_bounds = native_window_->GetBounds(); int screen_width = screen_bounds.width(); int screen_height = 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_EACH_OBSERVER(ImeWindowObserver, observers_, 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) { if (type == chrome::NOTIFICATION_APP_TERMINATING) 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::MoveContents(content::WebContents* source, const gfx::Rect& pos) { if (!native_window_) return; if (mode_ == NORMAL) { native_window_->SetBounds(pos); return; } // Follow-cursor window needs to remain the x/y and only allow JS to // change the size. gfx::Rect bounds = native_window_->GetBounds(); bounds.set_width(pos.width()); bounds.set_height(pos.height()); native_window_->SetBounds(bounds); } bool ImeWindow::IsPopupOrPanel(const content::WebContents* source) const { return true; } } // namespace ui