mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			335 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			335 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright (c) 2015 The Chromium Embedded Framework 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 "tests/cefclient/browser/preferences_test.h"
 | |
| 
 | |
| #include <sstream>
 | |
| #include <string>
 | |
| #include <vector>
 | |
| 
 | |
| #include "include/base/cef_logging.h"
 | |
| #include "include/cef_command_line.h"
 | |
| #include "include/cef_parser.h"
 | |
| #include "tests/cefclient/browser/test_runner.h"
 | |
| 
 | |
| namespace client {
 | |
| namespace preferences_test {
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| const char kTestUrlPath[] = "/preferences";
 | |
| 
 | |
| // Application-specific error codes.
 | |
| const int kMessageFormatError = 1;
 | |
| const int kPreferenceApplicationError = 1;
 | |
| 
 | |
| // Common to all messages.
 | |
| const char kNameKey[] = "name";
 | |
| const char kNameValueGet[] = "preferences_get";
 | |
| const char kNameValueSet[] = "preferences_set";
 | |
| const char kNameValueState[] = "preferences_state";
 | |
| 
 | |
| // Used with "preferences_get" messages.
 | |
| const char kIncludeDefaultsKey[] = "include_defaults";
 | |
| 
 | |
| // Used with "preferences_set" messages.
 | |
| const char kPreferencesKey[] = "preferences";
 | |
| 
 | |
| // Handle messages in the browser process. Only accessed on the UI thread.
 | |
| class Handler : public CefMessageRouterBrowserSide::Handler {
 | |
|  public:
 | |
|   typedef std::vector<std::string> NameVector;
 | |
| 
 | |
|   Handler() { CEF_REQUIRE_UI_THREAD(); }
 | |
| 
 | |
|   // Called due to cefQuery execution in preferences.html.
 | |
|   bool OnQuery(CefRefPtr<CefBrowser> browser,
 | |
|                CefRefPtr<CefFrame> frame,
 | |
|                int64 query_id,
 | |
|                const CefString& request,
 | |
|                bool persistent,
 | |
|                CefRefPtr<Callback> callback) override {
 | |
|     CEF_REQUIRE_UI_THREAD();
 | |
| 
 | |
|     // Only handle messages from the test URL.
 | |
|     const std::string& url = frame->GetURL();
 | |
|     if (!test_runner::IsTestURL(url, kTestUrlPath))
 | |
|       return false;
 | |
| 
 | |
|     // Parse |request| as a JSON dictionary.
 | |
|     CefRefPtr<CefDictionaryValue> request_dict = ParseJSON(request);
 | |
|     if (!request_dict) {
 | |
|       callback->Failure(kMessageFormatError, "Incorrect message format");
 | |
|       return true;
 | |
|     }
 | |
| 
 | |
|     // Verify the "name" key.
 | |
|     if (!VerifyKey(request_dict, kNameKey, VTYPE_STRING, callback))
 | |
|       return true;
 | |
| 
 | |
|     const std::string& message_name = request_dict->GetString(kNameKey);
 | |
|     if (message_name == kNameValueGet) {
 | |
|       // JavaScript is requesting a JSON representation of the preferences tree.
 | |
| 
 | |
|       // Verify the "include_defaults" key.
 | |
|       if (!VerifyKey(request_dict, kIncludeDefaultsKey, VTYPE_BOOL, callback))
 | |
|         return true;
 | |
| 
 | |
|       const bool include_defaults = request_dict->GetBool(kIncludeDefaultsKey);
 | |
| 
 | |
|       OnPreferencesGet(browser, include_defaults, callback);
 | |
| 
 | |
|       return true;
 | |
|     } else if (message_name == kNameValueSet) {
 | |
|       // JavaScript is requesting that preferences be updated to match the
 | |
|       // specified JSON representation.
 | |
| 
 | |
|       // Verify the "preferences" key.
 | |
|       if (!VerifyKey(request_dict, kPreferencesKey, VTYPE_DICTIONARY, callback))
 | |
|         return true;
 | |
| 
 | |
|       CefRefPtr<CefDictionaryValue> preferences =
 | |
|           request_dict->GetDictionary(kPreferencesKey);
 | |
| 
 | |
|       OnPreferencesSet(browser, preferences, callback);
 | |
| 
 | |
|       return true;
 | |
|     } else if (message_name == kNameValueState) {
 | |
|       // JavaScript is requesting global state information.
 | |
| 
 | |
|       OnPreferencesState(browser, callback);
 | |
| 
 | |
|       return true;
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   // Execute |callback| with the preferences dictionary as a JSON string.
 | |
|   static void OnPreferencesGet(CefRefPtr<CefBrowser> browser,
 | |
|                                bool include_defaults,
 | |
|                                CefRefPtr<Callback> callback) {
 | |
|     CefRefPtr<CefRequestContext> context =
 | |
|         browser->GetHost()->GetRequestContext();
 | |
| 
 | |
|     // Retrieve all preference values.
 | |
|     CefRefPtr<CefDictionaryValue> prefs =
 | |
|         context->GetAllPreferences(include_defaults);
 | |
| 
 | |
|     // Serialize the preferences to JSON and return to the JavaScript caller.
 | |
|     callback->Success(GetJSON(prefs));
 | |
|   }
 | |
| 
 | |
|   // Set preferences based on the contents of |preferences|. Execute |callback|
 | |
|   // with a descriptive result message.
 | |
|   static void OnPreferencesSet(CefRefPtr<CefBrowser> browser,
 | |
|                                CefRefPtr<CefDictionaryValue> preferences,
 | |
|                                CefRefPtr<Callback> callback) {
 | |
|     CefRefPtr<CefRequestContext> context =
 | |
|         browser->GetHost()->GetRequestContext();
 | |
| 
 | |
|     CefRefPtr<CefValue> value = CefValue::Create();
 | |
|     value->SetDictionary(preferences);
 | |
| 
 | |
|     std::string error;
 | |
|     NameVector changed_names;
 | |
| 
 | |
|     // Apply preferences. This may result in errors.
 | |
|     const bool success =
 | |
|         ApplyPrefs(context, std::string(), value, error, changed_names);
 | |
| 
 | |
|     // Create a message that accurately represents the result.
 | |
|     std::string message;
 | |
|     if (!changed_names.empty()) {
 | |
|       std::stringstream ss;
 | |
|       ss << "Successfully changed " << changed_names.size() << " preferences; ";
 | |
|       for (size_t i = 0; i < changed_names.size(); ++i) {
 | |
|         ss << changed_names[i];
 | |
|         if (i < changed_names.size() - 1)
 | |
|           ss << ", ";
 | |
|       }
 | |
|       message = ss.str();
 | |
|     }
 | |
| 
 | |
|     if (!success) {
 | |
|       DCHECK(!error.empty());
 | |
|       if (!message.empty())
 | |
|         message += "\n";
 | |
|       message += error;
 | |
|     }
 | |
| 
 | |
|     if (changed_names.empty()) {
 | |
|       if (!message.empty())
 | |
|         message += "\n";
 | |
|       message += "No preferences changed.";
 | |
|     }
 | |
| 
 | |
|     // Return the message to the JavaScript caller.
 | |
|     if (success)
 | |
|       callback->Success(message);
 | |
|     else
 | |
|       callback->Failure(kPreferenceApplicationError, message);
 | |
|   }
 | |
| 
 | |
|   // Execute |callback| with the global state dictionary as a JSON string.
 | |
|   static void OnPreferencesState(CefRefPtr<CefBrowser> browser,
 | |
|                                  CefRefPtr<Callback> callback) {
 | |
|     CefRefPtr<CefCommandLine> command_line =
 | |
|         CefCommandLine::GetGlobalCommandLine();
 | |
| 
 | |
|     CefRefPtr<CefDictionaryValue> dict = CefDictionaryValue::Create();
 | |
| 
 | |
|     // If spell checking is disabled via the command-line then it cannot be
 | |
|     // enabled via preferences.
 | |
|     dict->SetBool("spellcheck_disabled",
 | |
|                   command_line->HasSwitch("disable-spell-checking"));
 | |
| 
 | |
|     // If proxy settings are configured via the command-line then they cannot
 | |
|     // be modified via preferences.
 | |
|     dict->SetBool("proxy_configured",
 | |
|                   command_line->HasSwitch("no-proxy-server") ||
 | |
|                       command_line->HasSwitch("proxy-auto-detect") ||
 | |
|                       command_line->HasSwitch("proxy-pac-url") ||
 | |
|                       command_line->HasSwitch("proxy-server"));
 | |
| 
 | |
|     // If allow running insecure content is enabled via the command-line then it
 | |
|     // cannot be enabled via preferences.
 | |
|     dict->SetBool("allow_running_insecure_content",
 | |
|                   command_line->HasSwitch("allow-running-insecure-content"));
 | |
| 
 | |
|     // Serialize the state to JSON and return to the JavaScript caller.
 | |
|     callback->Success(GetJSON(dict));
 | |
|   }
 | |
| 
 | |
|   // Convert a JSON string to a dictionary value.
 | |
|   static CefRefPtr<CefDictionaryValue> ParseJSON(const CefString& string) {
 | |
|     CefRefPtr<CefValue> value = CefParseJSON(string, JSON_PARSER_RFC);
 | |
|     if (value.get() && value->GetType() == VTYPE_DICTIONARY)
 | |
|       return value->GetDictionary();
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Convert a dictionary value to a JSON string.
 | |
|   static CefString GetJSON(CefRefPtr<CefDictionaryValue> dictionary) {
 | |
|     CefRefPtr<CefValue> value = CefValue::Create();
 | |
|     value->SetDictionary(dictionary);
 | |
|     return CefWriteJSON(value, JSON_WRITER_DEFAULT);
 | |
|   }
 | |
| 
 | |
|   // Verify that |key| exists in |dictionary| and has type |value_type|. Fails
 | |
|   // |callback| and returns false on failure.
 | |
|   static bool VerifyKey(CefRefPtr<CefDictionaryValue> dictionary,
 | |
|                         const char* key,
 | |
|                         cef_value_type_t value_type,
 | |
|                         CefRefPtr<Callback> callback) {
 | |
|     if (!dictionary->HasKey(key) || dictionary->GetType(key) != value_type) {
 | |
|       callback->Failure(
 | |
|           kMessageFormatError,
 | |
|           "Missing or incorrectly formatted message key: " + std::string(key));
 | |
|       return false;
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // Apply preferences. Returns true on success. Returns false and sets |error|
 | |
|   // to a descriptive error string on failure. |changed_names| is the list of
 | |
|   // preferences that were successfully changed.
 | |
|   static bool ApplyPrefs(CefRefPtr<CefRequestContext> context,
 | |
|                          const std::string& name,
 | |
|                          CefRefPtr<CefValue> value,
 | |
|                          std::string& error,
 | |
|                          NameVector& changed_names) {
 | |
|     if (!name.empty() && context->HasPreference(name)) {
 | |
|       // The preference exists. Set the value.
 | |
|       return SetPref(context, name, value, error, changed_names);
 | |
|     }
 | |
| 
 | |
|     if (value->GetType() == VTYPE_DICTIONARY) {
 | |
|       // A dictionary type value that is not an existing preference. Try to set
 | |
|       // each of the elements individually.
 | |
|       CefRefPtr<CefDictionaryValue> dict = value->GetDictionary();
 | |
| 
 | |
|       CefDictionaryValue::KeyList keys;
 | |
|       dict->GetKeys(keys);
 | |
|       for (size_t i = 0; i < keys.size(); ++i) {
 | |
|         const std::string& key = keys[i];
 | |
|         const std::string& current_name = name.empty() ? key : name + "." + key;
 | |
|         if (!ApplyPrefs(context, current_name, dict->GetValue(key), error,
 | |
|                         changed_names)) {
 | |
|           return false;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       return true;
 | |
|     }
 | |
| 
 | |
|     error = "Trying to create an unregistered preference: " + name;
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // Set a specific preference value. Returns true if the value is set
 | |
|   // successfully or has not changed. If the value has changed then |name| will
 | |
|   // be added to |changed_names|. Returns false and sets |error| to a
 | |
|   // descriptive error string on failure.
 | |
|   static bool SetPref(CefRefPtr<CefRequestContext> context,
 | |
|                       const std::string& name,
 | |
|                       CefRefPtr<CefValue> value,
 | |
|                       std::string& error,
 | |
|                       NameVector& changed_names) {
 | |
|     CefRefPtr<CefValue> existing_value = context->GetPreference(name);
 | |
|     DCHECK(existing_value);
 | |
| 
 | |
|     if (value->GetType() == VTYPE_STRING &&
 | |
|         existing_value->GetType() != VTYPE_STRING) {
 | |
|       // Since |value| is coming from JSON all basic types will be represented
 | |
|       // as strings. Convert to the expected data type.
 | |
|       const std::string& string_val = value->GetString();
 | |
|       switch (existing_value->GetType()) {
 | |
|         case VTYPE_BOOL:
 | |
|           if (string_val == "true" || string_val == "1")
 | |
|             value->SetBool(true);
 | |
|           else if (string_val == "false" || string_val == "0")
 | |
|             value->SetBool(false);
 | |
|           break;
 | |
|         case VTYPE_INT:
 | |
|           value->SetInt(atoi(string_val.c_str()));
 | |
|           break;
 | |
|         case VTYPE_DOUBLE:
 | |
|           value->SetInt(atof(string_val.c_str()));
 | |
|           break;
 | |
|         default:
 | |
|           // Other types cannot be converted.
 | |
|           break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Nothing to do if the value hasn't changed.
 | |
|     if (existing_value->IsEqual(value))
 | |
|       return true;
 | |
| 
 | |
|     // Attempt to set the preference.
 | |
|     CefString error_str;
 | |
|     if (!context->SetPreference(name, value, error_str)) {
 | |
|       error = error_str.ToString() + ": " + name;
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     // The preference was set successfully.
 | |
|     changed_names.push_back(name);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   DISALLOW_COPY_AND_ASSIGN(Handler);
 | |
| };
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) {
 | |
|   handlers.insert(new Handler());
 | |
| }
 | |
| 
 | |
| }  // namespace preferences_test
 | |
| }  // namespace client
 |