Upstream version 9.37.195.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / input_ime / input_ime_api.cc
index adb2922..3b4c626 100644 (file)
@@ -6,16 +6,20 @@
 
 #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;
@@ -75,8 +79,16 @@ static void DispatchEventToExtension(Profile* profile,
   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
@@ -84,11 +96,18 @@ static void DispatchEventToExtension(Profile* profile,
 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() {}
@@ -164,13 +183,26 @@ class ImeObserver : public InputMethodEngineInterface::Observer {
     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));
@@ -268,9 +300,20 @@ class ImeObserver : public InputMethodEngineInterface::Observer {
   }
 
  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);
 };
@@ -284,96 +327,99 @@ InputImeEventRouter::GetInstance() {
   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;
 }
@@ -392,13 +438,7 @@ void InputImeEventRouter::OnKeyEventHandled(
   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(
@@ -418,7 +458,7 @@ InputImeEventRouter::InputImeEventRouter()
 
 InputImeEventRouter::~InputImeEventRouter() {}
 
-bool InputImeSetCompositionFunction::RunImpl() {
+bool InputImeSetCompositionFunction::RunSync() {
   InputMethodEngineInterface* engine =
       InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
   if (!engine) {
@@ -464,7 +504,7 @@ bool InputImeSetCompositionFunction::RunImpl() {
   return true;
 }
 
-bool InputImeClearCompositionFunction::RunImpl() {
+bool InputImeClearCompositionFunction::RunSync() {
   InputMethodEngineInterface* engine =
       InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
   if (!engine) {
@@ -482,7 +522,7 @@ bool InputImeClearCompositionFunction::RunImpl() {
   return true;
 }
 
-bool InputImeCommitTextFunction::RunImpl() {
+bool InputImeCommitTextFunction::RunSync() {
   // TODO(zork): Support committing when not active.
   InputMethodEngineInterface* engine =
       InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
@@ -501,7 +541,7 @@ bool InputImeCommitTextFunction::RunImpl() {
   return true;
 }
 
-bool InputImeHideInputViewFunction::RunImpl() {
+bool InputImeHideInputViewFunction::RunAsync() {
   InputMethodEngineInterface* engine =
       InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
   if (!engine) {
@@ -511,7 +551,7 @@ bool InputImeHideInputViewFunction::RunImpl() {
   return true;
 }
 
-bool InputImeSendKeyEventsFunction::RunImpl() {
+bool InputImeSendKeyEventsFunction::RunAsync() {
   scoped_ptr<SendKeyEvents::Params> parent_params(
       SendKeyEvents::Params::Create(*args_));
   const SendKeyEvents::Params::Parameters& params =
@@ -525,13 +565,14 @@ bool InputImeSendKeyEventsFunction::RunImpl() {
 
   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)
@@ -547,7 +588,7 @@ bool InputImeSendKeyEventsFunction::RunImpl() {
   return true;
 }
 
-bool InputImeSetCandidateWindowPropertiesFunction::RunImpl() {
+bool InputImeSetCandidateWindowPropertiesFunction::RunSync() {
   scoped_ptr<SetCandidateWindowProperties::Params> parent_params(
       SetCandidateWindowProperties::Params::Create(*args_));
   const SetCandidateWindowProperties::Params::Parameters&
@@ -622,8 +663,7 @@ bool InputImeSetCandidateWindowPropertiesFunction::RunImpl() {
   return true;
 }
 
-#if defined(OS_CHROMEOS)
-bool InputImeSetCandidatesFunction::RunImpl() {
+bool InputImeSetCandidatesFunction::RunSync() {
   InputMethodEngineInterface* engine =
       InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
   if (!engine) {
@@ -659,7 +699,7 @@ bool InputImeSetCandidatesFunction::RunImpl() {
   return true;
 }
 
-bool InputImeSetCursorPositionFunction::RunImpl() {
+bool InputImeSetCursorPositionFunction::RunSync() {
   InputMethodEngineInterface* engine =
       InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
   if (!engine) {
@@ -678,7 +718,7 @@ bool InputImeSetCursorPositionFunction::RunImpl() {
   return true;
 }
 
-bool InputImeSetMenuItemsFunction::RunImpl() {
+bool InputImeSetMenuItemsFunction::RunSync() {
   scoped_ptr<SetMenuItems::Params> parent_params(
       SetMenuItems::Params::Create(*args_));
   const SetMenuItems::Params::Parameters& params =
@@ -705,7 +745,7 @@ bool InputImeSetMenuItemsFunction::RunImpl() {
   return true;
 }
 
-bool InputImeUpdateMenuItemsFunction::RunImpl() {
+bool InputImeUpdateMenuItemsFunction::RunSync() {
   scoped_ptr<UpdateMenuItems::Params> parent_params(
       UpdateMenuItems::Params::Create(*args_));
   const UpdateMenuItems::Params::Parameters& params =
@@ -732,7 +772,7 @@ bool InputImeUpdateMenuItemsFunction::RunImpl() {
   return true;
 }
 
-bool InputImeDeleteSurroundingTextFunction::RunImpl() {
+bool InputImeDeleteSurroundingTextFunction::RunSync() {
   scoped_ptr<DeleteSurroundingText::Params> parent_params(
       DeleteSurroundingText::Params::Create(*args_));
   const DeleteSurroundingText::Params::Parameters& params =
@@ -751,64 +791,79 @@ bool InputImeDeleteSurroundingTextFunction::RunImpl() {
   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();
 }