summaryrefslogtreecommitdiffstats
path: root/ceee/ie/broker/cookie_api_module.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ceee/ie/broker/cookie_api_module.cc')
-rw-r--r--ceee/ie/broker/cookie_api_module.cc450
1 files changed, 450 insertions, 0 deletions
diff --git a/ceee/ie/broker/cookie_api_module.cc b/ceee/ie/broker/cookie_api_module.cc
new file mode 100644
index 0000000..2e7d59b
--- /dev/null
+++ b/ceee/ie/broker/cookie_api_module.cc
@@ -0,0 +1,450 @@
+// Copyright (c) 2010 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.
+//
+// Cookie API implementation.
+
+#include "ceee/ie/broker/cookie_api_module.h"
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/string_util.h"
+#include "ceee/common/com_utils.h"
+#include "ceee/common/process_utils_win.h"
+#include "ceee/common/window_utils.h"
+#include "ceee/common/windows_constants.h"
+#include "ceee/ie/broker/api_module_constants.h"
+#include "ceee/ie/broker/api_module_util.h"
+#include "ceee/ie/broker/window_api_module.h"
+#include "ceee/ie/common/api_registration.h"
+#include "chrome/browser/extensions/extension_cookies_api_constants.h"
+#include "chrome/common/extensions/extension_error_utils.h"
+
+namespace keys = extension_cookies_api_constants;
+
+namespace cookie_api {
+
+void RegisterInvocations(ApiDispatcher* dispatcher) {
+#define REGISTER_API_FUNCTION(func) do { dispatcher->RegisterInvocation(\
+ func##Function::function_name(), NewApiInvocation< func >); }\
+ while (false)
+ REGISTER_COOKIE_API_FUNCTIONS();
+#undef REGISTER_API_FUNCTION
+ // Now register the permanent event handler.
+ dispatcher->RegisterPermanentEventHandler(keys::kOnChanged,
+ CookieChanged::EventHandler);
+}
+
+bool CookieApiResult::CreateCookieValue(const CookieInfo& cookie_info) {
+ scoped_ptr<DictionaryValue> cookie(new DictionaryValue());
+ cookie->SetString(keys::kNameKey, com::ToString(cookie_info.name));
+ cookie->SetString(keys::kValueKey, com::ToString(cookie_info.value));
+ cookie->SetString(keys::kDomainKey, com::ToString(cookie_info.domain));
+ cookie->SetBoolean(keys::kHostOnlyKey, cookie_info.host_only == TRUE);
+ cookie->SetString(keys::kPathKey, com::ToString(cookie_info.path));
+ cookie->SetBoolean(keys::kSecureKey, cookie_info.secure == TRUE);
+ cookie->SetBoolean(keys::kHttpOnlyKey, cookie_info.http_only == TRUE);
+ cookie->SetBoolean(keys::kSessionKey, cookie_info.session == TRUE);
+ if (cookie_info.session == FALSE)
+ cookie->SetReal(keys::kExpirationDateKey, cookie_info.expiration_date);
+ cookie->SetString(keys::kStoreIdKey, com::ToString(cookie_info.store_id));
+ DCHECK(value() == NULL);
+ set_value(cookie.release());
+ return true;
+}
+
+HRESULT CookieApiResult::GetCookieInfo(
+ const std::string& url, const std::string& name, HWND window,
+ CookieInfo* cookie_info) {
+ // Get a tab window child of the cookie store window, so that we can properly
+ // access session cookies.
+ HWND tab_window = NULL;
+ if (!window_utils::FindDescendentWindow(
+ window, windows::kIeTabWindowClass, false, &tab_window) ||
+ tab_window == NULL) {
+ PostError("Failed to get tab window for a given cookie store.");
+ return E_FAIL;
+ }
+
+ CComPtr<ICeeeCookieExecutor> executor;
+ GetDispatcher()->GetExecutor(tab_window, IID_ICeeeCookieExecutor,
+ reinterpret_cast<void**>(&executor));
+ if (executor == NULL) {
+ LOG(WARNING) << "Failed to get an executor to get cookie info.";
+ PostError("Internal Error while getting cookie info.");
+ return E_FAIL;
+ }
+
+ HRESULT hr = executor->GetCookie(
+ CComBSTR(url.data()), CComBSTR(name.data()), cookie_info);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to get cookie info." << com::LogHr(hr);
+ PostError("Internal Error while getting cookie info.");
+ }
+ return hr;
+}
+
+bool CookieApiResult::CreateCookieStoreValue(DWORD process_id,
+ const WindowSet& windows) {
+ scoped_ptr<DictionaryValue> cookie_store(new DictionaryValue());
+ std::ostringstream store_id_stream;
+ store_id_stream << process_id;
+ // For IE CEEE, we use a string representation of the IE process ID as the
+ // cookie store ID.
+ cookie_store->SetString(keys::kIdKey, store_id_stream.str());
+ DCHECK(windows.size());
+ if (windows.size() == 0) {
+ PostError(api_module_constants::kInternalErrorError);
+ return false;
+ }
+ WindowSet::const_iterator iter = windows.begin();
+ // First register the cookie store once per process.
+ if (FAILED(RegisterCookieStore(*iter)))
+ return false;
+ // Now collect all tab IDs from each frame window into a single list.
+ scoped_ptr<ListValue> tab_ids(new ListValue());
+ for (; iter != windows.end(); ++iter) {
+ if (!AppendToTabIdList(*iter, tab_ids.get())) {
+ PostError(api_module_constants::kInternalErrorError);
+ return false;
+ }
+ }
+ cookie_store->Set(keys::kTabIdsKey, tab_ids.release());
+ set_value(cookie_store.release());
+ PostResult();
+ return true;
+}
+
+HRESULT CookieApiResult::RegisterCookieStore(HWND window) {
+ CComPtr<ICeeeCookieExecutor> executor;
+ GetDispatcher()->GetExecutor(window, IID_ICeeeCookieExecutor,
+ reinterpret_cast<void**>(&executor));
+ if (executor == NULL) {
+ LOG(WARNING) << "Failed to get an executor to register a cookie store.";
+ PostError(api_module_constants::kInternalErrorError);
+ return E_FAIL;
+ }
+ HRESULT hr = executor->RegisterCookieStore();
+ if (FAILED(hr)) {
+ // No DCHECK, this may happen if the window/thread dies on the way.
+ LOG(ERROR) << "Can't register cookie store. " << com::LogHr(hr);
+ PostError(api_module_constants::kInternalErrorError);
+ }
+ return hr;
+}
+
+HRESULT CookieApiResult::CookieStoreIsRegistered(HWND window) {
+ CComPtr<ICeeeCookieExecutor> executor;
+ GetDispatcher()->GetExecutor(window, IID_ICeeeCookieExecutor,
+ reinterpret_cast<void**>(&executor));
+ if (executor == NULL) {
+ LOG(WARNING) << "Failed to get an executor to register a cookie store.";
+ PostError(api_module_constants::kInternalErrorError);
+ return E_FAIL;
+ }
+ HRESULT hr = executor->CookieStoreIsRegistered();
+ if (FAILED(hr)) {
+ // No DCHECK, this may happen if the window/thread dies on the way.
+ LOG(ERROR) << "Error accessing cookie store. " << com::LogHr(hr);
+ PostError(api_module_constants::kInternalErrorError);
+ }
+ return hr;
+}
+
+bool CookieApiResult::AppendToTabIdList(HWND window, ListValue* tab_ids) {
+ CComPtr<ICeeeWindowExecutor> executor;
+ GetDispatcher()->GetExecutor(window, IID_ICeeeWindowExecutor,
+ reinterpret_cast<void**>(&executor));
+ if (executor == NULL) {
+ LOG(WARNING) << "Failed to get an executor to get window tabs.";
+ return false;
+ }
+ CComBSTR tab_ids_string;
+ HRESULT hr = executor->GetTabs(&tab_ids_string);
+ if (FAILED(hr)) {
+ // No DCHECK, this may happen if the window/thread dies on the way.
+ LOG(ERROR) << "Can't get tabs for window: " << std::hex << window <<
+ ". " << com::LogHr(hr);
+ return false;
+ }
+ scoped_ptr<ListValue> tabs_list;
+ if (!api_module_util::GetListFromJsonString(CW2A(tab_ids_string).m_psz,
+ &tabs_list)) {
+ NOTREACHED() << "Invalid tabs list BSTR: " << tab_ids_string;
+ return false;
+ }
+ size_t num_values = tabs_list->GetSize();
+ if (num_values % 2 != 0) {
+ // Values should come in pairs, one for the id and another one for the
+ // index.
+ NOTREACHED() << "Invalid tabs list BSTR: " << tab_ids_string;
+ return false;
+ }
+ for (size_t i = 0; i < num_values; i += 2) {
+ int tab_id = 0;
+ if (tabs_list->GetInteger(i, &tab_id))
+ tab_ids->Append(Value::CreateIntegerValue(tab_id));
+ }
+ return true;
+}
+
+void CookieApiResult::FindAllProcessesAndWindows(
+ ProcessWindowMap* all_windows) {
+ DCHECK(all_windows);
+ WindowSet ie_frame_windows;
+ window_utils::FindTopLevelWindows(windows::kIeFrameWindowClass,
+ &ie_frame_windows);
+ if (ie_frame_windows.empty())
+ return;
+
+ WindowSet::const_iterator iter = ie_frame_windows.begin();
+ for (; iter != ie_frame_windows.end(); ++iter) {
+ DWORD process_id = 0;
+ // Skip over windows with no thread.
+ if (::GetWindowThreadProcessId(*iter, &process_id) == 0)
+ continue;
+ DCHECK(process_id);
+
+ if (process_id != 0)
+ (*all_windows)[process_id].insert(*iter);
+ }
+}
+
+HWND CookieApiResult::GetWindowFromStoreId(const std::string& store_id,
+ bool allow_unregistered_store) {
+ DWORD store_process_id = 0;
+ std::istringstream store_id_stream(store_id);
+ store_id_stream >> store_process_id;
+ if (!store_process_id) {
+ PostError(ExtensionErrorUtils::FormatErrorMessage(
+ keys::kInvalidStoreIdError, store_id));
+ return NULL;
+ }
+
+ WindowSet ie_frame_windows;
+ window_utils::FindTopLevelWindows(windows::kIeFrameWindowClass,
+ &ie_frame_windows);
+
+ WindowSet::const_iterator iter = ie_frame_windows.begin();
+ for (; iter != ie_frame_windows.end(); ++iter) {
+ DWORD process_id = 0;
+ if (::GetWindowThreadProcessId(*iter, &process_id) != 0 &&
+ process_id == store_process_id) {
+ if (allow_unregistered_store)
+ return *iter;
+ HRESULT hr = CookieStoreIsRegistered(*iter);
+ // If the above call failed, an error has already been posted.
+ if (FAILED(hr)) {
+ return NULL;
+ } else if (hr == S_OK) {
+ return *iter;
+ }
+ }
+ }
+ // Matching window not found.
+ PostError(ExtensionErrorUtils::FormatErrorMessage(
+ keys::kInvalidStoreIdError, store_id));
+ return NULL;
+}
+
+void GetCookie::Execute(const ListValue& args, int request_id) {
+ scoped_ptr<CookieApiResult> result(CreateApiResult(request_id));
+
+ DictionaryValue* details;
+ if (!args.GetDictionary(0, &details)) {
+ result->PostError(api_module_constants::kInvalidArgumentsError);
+ return;
+ }
+
+ std::string url;
+ if (!details->GetString(keys::kUrlKey, &url)) {
+ result->PostError(api_module_constants::kInvalidArgumentsError);
+ return;
+ }
+ if (!GURL(url).is_valid()) {
+ result->PostError(ExtensionErrorUtils::FormatErrorMessage(
+ keys::kInvalidUrlError, url));
+ return;
+ }
+ // TODO(cindylau@chromium.org): Add extension host permissions
+ // checks against the URL.
+
+ std::string name;
+ if (!details->GetString(keys::kNameKey, &name)) {
+ result->PostError(api_module_constants::kInvalidArgumentsError);
+ return;
+ }
+
+ HWND cookie_store_window = NULL;
+ if (details->HasKey(keys::kStoreIdKey)) {
+ std::string store_id;
+ if (!details->GetString(keys::kStoreIdKey, &store_id)) {
+ result->PostError(api_module_constants::kInvalidArgumentsError);
+ return;
+ }
+ cookie_store_window = result->GetWindowFromStoreId(store_id, false);
+ // If no window was found, an error has already been posted.
+ if (!cookie_store_window)
+ return;
+ } else {
+ // The store ID was unspecified or isn't a registered cookie store
+ // ID. Use the current execution context's cookie store by default.
+ // TODO(cindylau@chromium.org): We currently don't have a way to
+ // get the current execution context, so we are using the top IE
+ // window for now.
+ cookie_store_window = window_api::WindowApiResult::TopIeWindow();
+ }
+ DCHECK(cookie_store_window);
+
+ CookieInfo cookie_info;
+ HRESULT hr = result->GetCookieInfo(url, name, cookie_store_window,
+ &cookie_info);
+ // If the call failed, an error has already been posted.
+ if (FAILED(hr))
+ return;
+ if (hr == S_OK) {
+ DCHECK(WideToASCII(com::ToString(cookie_info.name)) == name);
+ if (!result->CreateCookieValue(cookie_info))
+ return;
+ }
+ result->PostResult();
+}
+
+void GetAllCookies::Execute(const ListValue& args, int request_id) {
+ scoped_ptr<CookieApiResult> result(CreateApiResult(request_id));
+ result->PostError("Not implemented.");
+}
+
+void SetCookie::Execute(const ListValue& args, int request_id) {
+ scoped_ptr<CookieApiResult> result(CreateApiResult(request_id));
+ result->PostError("Not implemented.");
+}
+
+void RemoveCookie::Execute(const ListValue& args, int request_id) {
+ scoped_ptr<CookieApiResult> result(CreateApiResult(request_id));
+ result->PostError("Not implemented.");
+}
+
+void GetAllCookieStores::Execute(const ListValue& args, int request_id) {
+ scoped_ptr<IterativeCookieApiResult> result(CreateApiResult(request_id));
+
+ // TODO(cindylau@chromium.org): Restrict incognito (InPrivate)
+ // windows if incognito is not enabled for the extension. Right now
+ // CEEE has no mechanism to discover the extension's
+ // incognito-enabled setting, so adding support here is premature.
+ CookieApiResult::ProcessWindowMap all_windows;
+ CookieApiResult::FindAllProcessesAndWindows(&all_windows);
+
+ if (all_windows.empty()) {
+ result->FlushAllPosts();
+ return;
+ }
+
+ CookieApiResult::ProcessWindowMap::const_iterator iter = all_windows.begin();
+ for (; iter != all_windows.end(); ++iter) {
+ bool created_ok = result->CreateCookieStoreValue(iter->first, iter->second);
+ LOG_IF(WARNING, !created_ok) << "Could not create cookie store value:"
+ << result->LastError();
+ }
+
+ if (result->IsEmpty()) // This is an error!
+ result->PostError(keys::kNoCookieStoreFoundError);
+
+ result->FlushAllPosts();
+}
+
+// Static wrapper for the real event handler implementation.
+bool CookieChanged::EventHandler(const std::string& input_args,
+ std::string* converted_args,
+ ApiDispatcher* dispatcher) {
+ CookieChanged event_handler;
+ return event_handler.EventHandlerImpl(input_args, converted_args);
+}
+
+// Handles a cookies.onChanged event by verifying that the store ID is
+// registered. If not, this function registers the store ID.
+bool CookieChanged::EventHandlerImpl(const std::string& input_args,
+ std::string* converted_args) {
+ DCHECK(converted_args);
+ // We don't need to modify the arguments, we only need to verify that the
+ // store ID is registered.
+ *converted_args = input_args;
+
+ // Get the cookie info from the input arguments.
+ scoped_ptr<Value> input_val(base::JSONReader::Read(input_args, true));
+ DCHECK(input_val.get() != NULL);
+ if (input_val == NULL) {
+ LOG(ERROR) << "Invalid Arguments sent to CookieChangedEventHandler()";
+ return false;
+ }
+
+ ListValue* input_list = static_cast<ListValue*>(input_val.get());
+ DCHECK(input_list && input_list->GetSize() == 1);
+
+ DictionaryValue* changeInfo = NULL;
+ bool success = input_list->GetDictionary(0, &changeInfo);
+ DictionaryValue* cookie = NULL;
+ if (success && changeInfo && changeInfo->HasKey(keys::kCookieKey))
+ success = changeInfo->GetDictionary(keys::kCookieKey, &cookie);
+ if (!success || cookie == NULL) {
+ NOTREACHED() << "Failed to get the cookie value from the list of args.";
+ return false;
+ }
+ std::string store_id;
+ if (cookie->HasKey(keys::kStoreIdKey))
+ success = cookie->GetString(keys::kStoreIdKey, &store_id);
+ if (!success || store_id.size() == 0) {
+ NOTREACHED() << "Failed to get the store ID value from the cookie arg.";
+ return false;
+ }
+
+ scoped_ptr<CookieApiResult> api_result(CreateApiResult());
+ HWND store_window = api_result->GetWindowFromStoreId(store_id, true);
+ if (store_window == NULL) {
+ // Error was already logged by GetWindowFromStoreId.
+ return false;
+ }
+ HRESULT is_registered = api_result->CookieStoreIsRegistered(store_window);
+ if (FAILED(is_registered)) {
+ // Error was already logged by CookieStoreIsRegistered.
+ return false;
+ }
+ if (is_registered == S_FALSE) {
+ // The store ID has not yet been queried by the user; register it here
+ // before exposing it to the user.
+ is_registered = api_result->RegisterCookieStore(store_window);
+ }
+ if (is_registered != S_OK) {
+ // Any errors should have already been logged by RegisterCookieStore.
+ return false;
+ }
+
+ return true;
+}
+
+CookieInfo::CookieInfo() {
+ name = NULL;
+ value = NULL;
+ domain = NULL;
+ host_only = false;
+ path = NULL;
+ secure = false;
+ http_only = false;
+ session = false;
+ expiration_date = 0;
+ store_id = NULL;
+}
+
+CookieInfo::~CookieInfo() {
+ // SysFreeString accepts NULL pointers.
+ ::SysFreeString(name);
+ ::SysFreeString(value);
+ ::SysFreeString(domain);
+ ::SysFreeString(path);
+ ::SysFreeString(store_id);
+}
+
+} // namespace cookie_api