#include "base/strings/string_number_conversions.h"
#include "base/values.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/chromeos/input_method/input_method_engine.h"
-#include "chrome/browser/extensions/extension_function_registry.h"
-#include "chrome/browser/extensions/extension_system.h"
-#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/extensions/api/input_ime.h"
#include "chrome/common/extensions/api/input_ime/input_components_handler.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_source.h"
#include "extensions/browser/event_router.h"
+#include "extensions/browser/extension_function_registry.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/manifest_handlers/background_info.h"
+
+#if defined(USE_X11)
+#include "chrome/browser/chromeos/input_method/input_method_engine.h"
+#endif
namespace input_ime = extensions::api::input_ime;
namespace KeyEventHandled = extensions::api::input_ime::KeyEventHandled;
scoped_ptr<extensions::Event> event(new extensions::Event(
event_name, args.Pass()));
event->restrict_to_browser_context = profile;
- extensions::ExtensionSystem::Get(profile)->event_router()->
- DispatchEventToExtension(extension_id, event.Pass());
+ extensions::EventRouter::Get(profile)
+ ->DispatchEventToExtension(extension_id, event.Pass());
+}
+
+void CallbackKeyEventHandle(chromeos::input_method::KeyEventHandle* key_data,
+ bool handled) {
+ base::Callback<void(bool consumed)>* callback =
+ reinterpret_cast<base::Callback<void(bool consumed)>*>(key_data);
+ callback->Run(handled);
+ delete callback;
}
} // namespace
namespace chromeos {
class ImeObserver : public InputMethodEngineInterface::Observer {
public:
- ImeObserver(Profile* profile, const std::string& extension_id,
- const std::string& engine_id) :
- profile_(profile),
- extension_id_(extension_id),
- engine_id_(engine_id) {
+ ImeObserver(Profile* profile, const std::string& extension_id)
+ : profile_(profile), extension_id_(extension_id), has_background_(false) {
+ extensions::ExtensionSystem* extension_system =
+ extensions::ExtensionSystem::Get(profile_);
+ ExtensionService* extension_service = extension_system->extension_service();
+ const extensions::Extension* extension =
+ extension_service->GetExtensionById(extension_id, false);
+ DCHECK(extension);
+ extensions::BackgroundInfo* info = static_cast<extensions::BackgroundInfo*>(
+ extension->GetManifestData("background"));
+ if (info)
+ has_background_ = info->has_background_page();
}
virtual ~ImeObserver() {}
if (profile_ == NULL || extension_id_.empty())
return;
- std::string request_id =
- extensions::InputImeEventRouter::GetInstance()->AddRequest(engine_id,
- key_data);
+ // If there is no listener for the event, no need to dispatch the event to
+ // extension. Instead, releases the key event for default system behavior.
+ if (!ShouldForwardKeyEvent()) {
+ // Continue processing the key event so that the physical keyboard can
+ // still work.
+ CallbackKeyEventHandle(key_data, false);
+ return;
+ }
+
+ extensions::InputImeEventRouter* ime_event_router =
+ extensions::InputImeEventRouter::GetInstance();
+
+ const std::string request_id =
+ ime_event_router->AddRequest(engine_id, key_data);
input_ime::KeyboardEvent key_data_value;
key_data_value.type = input_ime::KeyboardEvent::ParseType(event.type);
key_data_value.request_id = request_id;
+ if (!event.extension_id.empty())
+ key_data_value.extension_id.reset(new std::string(event.extension_id));
key_data_value.key = event.key;
key_data_value.code = event.code;
key_data_value.alt_key.reset(new bool(event.alt_key));
}
private:
+ // Returns true if the extension is ready to accept key event, otherwise
+ // returns false.
+ bool ShouldForwardKeyEvent() const {
+ // Need to check the background page first since the
+ // ExtensionHasEventListner returns true if the extension does not have a
+ // background page. See crbug.com/394682.
+ return has_background_ && extensions::EventRouter::Get(profile_)
+ ->ExtensionHasEventListener(extension_id_,
+ input_ime::OnKeyEvent::kEventName);
+ }
+
Profile* profile_;
std::string extension_id_;
- std::string engine_id_;
+ bool has_background_;
DISALLOW_COPY_AND_ASSIGN(ImeObserver);
};
return Singleton<InputImeEventRouter>::get();
}
-#if defined(OS_CHROMEOS)
bool InputImeEventRouter::RegisterIme(
Profile* profile,
const std::string& extension_id,
const extensions::InputComponentInfo& component) {
+#if defined(USE_X11)
VLOG(1) << "RegisterIme: " << extension_id << " id: " << component.id;
- std::map<std::string, InputMethodEngineInterface*>& engine_map =
- engines_[extension_id];
-
- std::map<std::string, InputMethodEngineInterface*>::iterator engine_ix =
- engine_map.find(component.id);
- if (engine_ix != engine_map.end())
- return false;
-
- chromeos::ImeObserver* observer = new chromeos::ImeObserver(profile,
- extension_id,
- component.id);
std::vector<std::string> layouts;
layouts.assign(component.layouts.begin(), component.layouts.end());
std::vector<std::string> languages;
languages.assign(component.languages.begin(), component.languages.end());
+ // Ideally Observer should be per (extension_id + Profile), and multiple
+ // InputMethodEngine can share one Observer. But it would become tricky
+ // to maintain an internal map for observers which does nearly nothing
+ // but just make sure they can properly deleted.
+ // Making Obesrver per InputMethodEngine can make things cleaner.
+ scoped_ptr<chromeos::InputMethodEngineInterface::Observer> observer(
+ new chromeos::ImeObserver(profile, extension_id));
chromeos::InputMethodEngine* engine = new chromeos::InputMethodEngine();
- engine->Initialize(observer, component.name.c_str(), extension_id.c_str(),
- component.id.c_str(), languages, layouts,
- component.options_page_url, component.input_view_url);
- engine_map[component.id] = engine;
-
- std::map<std::string, chromeos::ImeObserver*>& observer_list =
- observers_[extension_id];
-
- observer_list[component.id] = observer;
+ engine->Initialize(profile,
+ observer.Pass(),
+ component.name.c_str(),
+ extension_id.c_str(),
+ component.id.c_str(),
+ languages,
+ layouts,
+ component.options_page_url,
+ component.input_view_url);
+ profile_engine_map_[profile][extension_id][component.id] = engine;
return true;
+#else
+ // TODO(spang): IME support under ozone.
+ NOTIMPLEMENTED();
+ return false;
+#endif
}
-void InputImeEventRouter::UnregisterAllImes(
- Profile* profile, const std::string& extension_id) {
- std::map<std::string,
- std::map<std::string,
- InputMethodEngineInterface*> >::iterator engine_map =
- engines_.find(extension_id);
- if (engine_map != engines_.end()) {
- STLDeleteContainerPairSecondPointers(engine_map->second.begin(),
- engine_map->second.end());
- engines_.erase(engine_map);
- }
-
- std::map<std::string,
- std::map<std::string,
- chromeos::ImeObserver*> >::iterator observer_list =
- observers_.find(extension_id);
- if (observer_list != observers_.end()) {
- STLDeleteContainerPairSecondPointers(observer_list->second.begin(),
- observer_list->second.end());
- observers_.erase(observer_list);
- }
+void InputImeEventRouter::UnregisterAllImes(const std::string& extension_id) {
+ Profile* profile = ProfileManager::GetActiveUserProfile();
+ ProfileEngineMap::iterator extension_map =
+ profile_engine_map_.find(profile);
+ if (extension_map == profile_engine_map_.end())
+ return;
+ ExtensionMap::iterator engine_map = extension_map->second.find(extension_id);
+ if (engine_map == extension_map->second.end())
+ return;
+ STLDeleteContainerPairSecondPointers(engine_map->second.begin(),
+ engine_map->second.end());
+ extension_map->second.erase(extension_id);
+ profile_engine_map_.erase(profile);
}
-#endif
InputMethodEngineInterface* InputImeEventRouter::GetEngine(
const std::string& extension_id, const std::string& engine_id) {
- std::map<std::string,
- std::map<std::string, InputMethodEngineInterface*> >::const_iterator
- engine_list = engines_.find(extension_id);
- if (engine_list != engines_.end()) {
- std::map<std::string, InputMethodEngineInterface*>::const_iterator
- engine_ix = engine_list->second.find(engine_id);
- if (engine_ix != engine_list->second.end())
- return engine_ix->second;
- }
- return NULL;
+ // IME can only work on active user profile.
+ Profile* profile = ProfileManager::GetActiveUserProfile();
+
+ ProfileEngineMap::const_iterator extension_map =
+ profile_engine_map_.find(profile);
+ if (extension_map == profile_engine_map_.end())
+ return NULL;
+ ExtensionMap::const_iterator engine_map =
+ extension_map->second.find(extension_id);
+ if (engine_map == extension_map->second.end())
+ return NULL;
+ EngineMap::const_iterator engine = engine_map->second.find(engine_id);
+ if (engine == engine_map->second.end())
+ return NULL;
+ return engine->second;
}
InputMethodEngineInterface* InputImeEventRouter::GetActiveEngine(
const std::string& extension_id) {
- std::map<std::string,
- std::map<std::string, InputMethodEngineInterface*> >::const_iterator
- engine_list = engines_.find(extension_id);
- if (engine_list != engines_.end()) {
- std::map<std::string, InputMethodEngineInterface*>::const_iterator
- engine_ix;
- for (engine_ix = engine_list->second.begin();
- engine_ix != engine_list->second.end();
- ++engine_ix) {
- if (engine_ix->second->IsActive())
- return engine_ix->second;
- }
+ // IME can only work on active user profile.
+ Profile* profile = ProfileManager::GetActiveUserProfile();
+
+ ProfileEngineMap::const_iterator extension_map =
+ profile_engine_map_.find(profile);
+ if (extension_map == profile_engine_map_.end())
+ return NULL;
+ ExtensionMap::const_iterator engine_map =
+ extension_map->second.find(extension_id);
+ if (engine_map == extension_map->second.end())
+ return NULL;
+
+ for (EngineMap::const_iterator i = engine_map->second.begin();
+ i != engine_map->second.end();
+ ++i) {
+ if (i->second->IsActive())
+ return i->second;
}
return NULL;
}
chromeos::input_method::KeyEventHandle* key_data = request->second.second;
request_map_.erase(request);
- InputMethodEngineInterface* engine = GetEngine(extension_id, engine_id);
- if (!engine) {
- LOG(ERROR) << "Engine does not exist: " << engine_id;
- return;
- }
-
- engine->KeyEventDone(key_data, handled);
+ CallbackKeyEventHandle(key_data, handled);
}
std::string InputImeEventRouter::AddRequest(
InputImeEventRouter::~InputImeEventRouter() {}
-bool InputImeSetCompositionFunction::RunImpl() {
+bool InputImeSetCompositionFunction::RunSync() {
InputMethodEngineInterface* engine =
InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
if (!engine) {
return true;
}
-bool InputImeClearCompositionFunction::RunImpl() {
+bool InputImeClearCompositionFunction::RunSync() {
InputMethodEngineInterface* engine =
InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
if (!engine) {
return true;
}
-bool InputImeCommitTextFunction::RunImpl() {
+bool InputImeCommitTextFunction::RunSync() {
// TODO(zork): Support committing when not active.
InputMethodEngineInterface* engine =
InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
return true;
}
-bool InputImeHideInputViewFunction::RunImpl() {
+bool InputImeHideInputViewFunction::RunAsync() {
InputMethodEngineInterface* engine =
InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
if (!engine) {
return true;
}
-bool InputImeSendKeyEventsFunction::RunImpl() {
+bool InputImeSendKeyEventsFunction::RunAsync() {
scoped_ptr<SendKeyEvents::Params> parent_params(
SendKeyEvents::Params::Create(*args_));
const SendKeyEvents::Params::Parameters& params =
const std::vector<linked_ptr<input_ime::KeyboardEvent> >& key_data =
params.key_data;
- std::vector<chromeos::InputMethodEngine::KeyboardEvent> key_data_out;
+ std::vector<chromeos::InputMethodEngineInterface::KeyboardEvent> key_data_out;
for (size_t i = 0; i < key_data.size(); ++i) {
- chromeos::InputMethodEngine::KeyboardEvent event;
+ chromeos::InputMethodEngineInterface::KeyboardEvent event;
event.type = input_ime::KeyboardEvent::ToString(key_data[i]->type);
event.key = key_data[i]->key;
event.code = key_data[i]->code;
+ event.key_code = key_data[i]->key_code.get() ? *(key_data[i]->key_code) : 0;
if (key_data[i]->alt_key)
event.alt_key = *(key_data[i]->alt_key);
if (key_data[i]->ctrl_key)
return true;
}
-bool InputImeSetCandidateWindowPropertiesFunction::RunImpl() {
+bool InputImeSetCandidateWindowPropertiesFunction::RunSync() {
scoped_ptr<SetCandidateWindowProperties::Params> parent_params(
SetCandidateWindowProperties::Params::Create(*args_));
const SetCandidateWindowProperties::Params::Parameters&
return true;
}
-#if defined(OS_CHROMEOS)
-bool InputImeSetCandidatesFunction::RunImpl() {
+bool InputImeSetCandidatesFunction::RunSync() {
InputMethodEngineInterface* engine =
InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
if (!engine) {
return true;
}
-bool InputImeSetCursorPositionFunction::RunImpl() {
+bool InputImeSetCursorPositionFunction::RunSync() {
InputMethodEngineInterface* engine =
InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
if (!engine) {
return true;
}
-bool InputImeSetMenuItemsFunction::RunImpl() {
+bool InputImeSetMenuItemsFunction::RunSync() {
scoped_ptr<SetMenuItems::Params> parent_params(
SetMenuItems::Params::Create(*args_));
const SetMenuItems::Params::Parameters& params =
return true;
}
-bool InputImeUpdateMenuItemsFunction::RunImpl() {
+bool InputImeUpdateMenuItemsFunction::RunSync() {
scoped_ptr<UpdateMenuItems::Params> parent_params(
UpdateMenuItems::Params::Create(*args_));
const UpdateMenuItems::Params::Parameters& params =
return true;
}
-bool InputImeDeleteSurroundingTextFunction::RunImpl() {
+bool InputImeDeleteSurroundingTextFunction::RunSync() {
scoped_ptr<DeleteSurroundingText::Params> parent_params(
DeleteSurroundingText::Params::Create(*args_));
const DeleteSurroundingText::Params::Parameters& params =
return true;
}
-bool InputImeKeyEventHandledFunction::RunImpl() {
+bool InputImeKeyEventHandledFunction::RunAsync() {
scoped_ptr<KeyEventHandled::Params> params(
KeyEventHandled::Params::Create(*args_));
InputImeEventRouter::GetInstance()->OnKeyEventHandled(
extension_id(), params->request_id, params->response);
return true;
}
-#endif
-InputImeAPI::InputImeAPI(Profile* profile)
- : profile_(profile) {
- registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
- content::Source<Profile>(profile));
- registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
- content::Source<Profile>(profile));
+InputImeAPI::InputImeAPI(content::BrowserContext* context)
+ : browser_context_(context), extension_registry_observer_(this) {
+ extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
+
+ EventRouter* event_router = EventRouter::Get(browser_context_);
+ event_router->RegisterObserver(this, input_ime::OnActivate::kEventName);
+ event_router->RegisterObserver(this, input_ime::OnFocus::kEventName);
}
InputImeAPI::~InputImeAPI() {
+ EventRouter::Get(browser_context_)->UnregisterObserver(this);
}
-static base::LazyInstance<ProfileKeyedAPIFactory<InputImeAPI> >
-g_factory = LAZY_INSTANCE_INITIALIZER;
+static base::LazyInstance<BrowserContextKeyedAPIFactory<InputImeAPI> >
+ g_factory = LAZY_INSTANCE_INITIALIZER;
// static
-ProfileKeyedAPIFactory<InputImeAPI>* InputImeAPI::GetFactoryInstance() {
+BrowserContextKeyedAPIFactory<InputImeAPI>* InputImeAPI::GetFactoryInstance() {
return g_factory.Pointer();
}
-void InputImeAPI::Observe(int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) {
- if (type == chrome::NOTIFICATION_EXTENSION_LOADED) {
- const Extension* extension =
- content::Details<const Extension>(details).ptr();
- const std::vector<InputComponentInfo>* input_components =
- extensions::InputComponents::GetInputComponents(extension);
- if (!input_components)
- return;
- for (std::vector<extensions::InputComponentInfo>::const_iterator component =
- input_components->begin(); component != input_components->end();
- ++component) {
- if (component->type == extensions::INPUT_COMPONENT_TYPE_IME) {
- input_ime_event_router()->RegisterIme(
- profile_, extension->id(), *component);
- }
+void InputImeAPI::OnExtensionLoaded(content::BrowserContext* browser_context,
+ const Extension* extension) {
+ const std::vector<InputComponentInfo>* input_components =
+ extensions::InputComponents::GetInputComponents(extension);
+ if (!input_components)
+ return;
+ for (std::vector<extensions::InputComponentInfo>::const_iterator component =
+ input_components->begin();
+ component != input_components->end();
+ ++component) {
+ if (component->type == extensions::INPUT_COMPONENT_TYPE_IME) {
+ // If |browser_context| looks like signin profile, use the real signin
+ // profile. This is because IME extensions for signin profile are run
+ // in Off-The-Record profile, based on given static defaults.
+ // So if |profile_| is signin profile, we need to make sure
+ // the router/observer runs under its incognito profile, because the
+ // component extensions were installed under its incognito profile.
+ Profile* profile = Profile::FromBrowserContext(browser_context);
+ if (chromeos::ProfileHelper::IsSigninProfile(profile))
+ profile = chromeos::ProfileHelper::GetSigninProfile();
+ input_ime_event_router()->RegisterIme(
+ profile, extension->id(), *component);
}
- } else if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
- const Extension* extension =
- content::Details<const UnloadedExtensionInfo>(details)->extension;
- const std::vector<InputComponentInfo>* input_components =
- extensions::InputComponents::GetInputComponents(extension);
- if (!input_components)
- return;
- if (input_components->size() > 0)
- input_ime_event_router()->UnregisterAllImes(profile_, extension->id());
}
}
+void InputImeAPI::OnExtensionUnloaded(content::BrowserContext* browser_context,
+ const Extension* extension,
+ UnloadedExtensionInfo::Reason reason) {
+ const std::vector<InputComponentInfo>* input_components =
+ extensions::InputComponents::GetInputComponents(extension);
+ if (!input_components)
+ return;
+ if (input_components->size() > 0)
+ input_ime_event_router()->UnregisterAllImes(extension->id());
+}
+
+void InputImeAPI::OnListenerAdded(const EventListenerInfo& details) {
+ InputMethodEngineInterface* engine =
+ input_ime_event_router()->GetActiveEngine(details.extension_id);
+ if (engine)
+ engine->NotifyImeReady();
+}
+
InputImeEventRouter* InputImeAPI::input_ime_event_router() {
return InputImeEventRouter::GetInstance();
}