1/* 2 * Copyright (C) 2012 Samsung Electronics 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this program; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 * 19 */ 20 21#include "config.h" 22#include "ewk_context.h" 23 24#include "BatteryProvider.h" 25#include "ContextHistoryClientEfl.h" 26#include "DownloadManagerEfl.h" 27#include "RequestManagerClientEfl.h" 28#include "WKAPICast.h" 29#include "WKContextPrivate.h" 30#include "WKContextSoup.h" 31#include "WKNumber.h" 32#include "WKString.h" 33#include "WebContext.h" 34#include "WebIconDatabase.h" 35#include "ewk_application_cache_manager_private.h" 36#include "ewk_context_private.h" 37#include "ewk_cookie_manager_private.h" 38#include "ewk_database_manager_private.h" 39#include "ewk_favicon_database_private.h" 40#include "ewk_private.h" 41#include "ewk_storage_manager_private.h" 42#include "ewk_url_scheme_request_private.h" 43#include <JavaScriptCore/JSContextRef.h> 44#include <WebCore/FileSystem.h> 45#include <WebCore/IconDatabase.h> 46#include <WebCore/Language.h> 47#include <wtf/HashMap.h> 48#include <wtf/NeverDestroyed.h> 49#include <wtf/text/WTFString.h> 50 51#if ENABLE(SPELLCHECK) 52#include "TextCheckerClientEfl.h" 53#endif 54 55using namespace WebCore; 56using namespace WebKit; 57 58typedef HashMap<WKContextRef, EwkContext*> ContextMap; 59 60static inline ContextMap& contextMap() 61{ 62 static NeverDestroyed<ContextMap> map; 63 return map; 64} 65 66EwkContext::EwkContext(WKContextRef context) 67 : m_context(context) 68 , m_databaseManager(std::make_unique<EwkDatabaseManager>(WKContextGetDatabaseManager(context))) 69 , m_storageManager(std::make_unique<EwkStorageManager>(WKContextGetKeyValueStorageManager(context))) 70#if ENABLE(BATTERY_STATUS) 71 , m_batteryProvider(BatteryProvider::create(context)) 72#endif 73 , m_downloadManager(std::make_unique<DownloadManagerEfl>(context)) 74 , m_requestManagerClient(std::make_unique<RequestManagerClientEfl>(context)) 75 , m_historyClient(std::make_unique<ContextHistoryClientEfl>(context)) 76 , m_jsGlobalContext(nullptr) 77{ 78 ContextMap::AddResult result = contextMap().add(context, this); 79 ASSERT_UNUSED(result, result.isNewEntry); 80 81#if ENABLE(MEMORY_SAMPLER) 82 static bool initializeMemorySampler = false; 83 static const char environmentVariable[] = "SAMPLE_MEMORY"; 84 85 if (!initializeMemorySampler && getenv(environmentVariable)) { 86 WKContextStartMemorySampler(context, adoptWK(WKDoubleCreate(0.0)).get()); 87 initializeMemorySampler = true; 88 } 89#endif 90 91#if ENABLE(SPELLCHECK) 92 // Load the default dictionary to show context menu spellchecking items 93 // independently of checking spelling while typing setting. 94 TextCheckerClientEfl::instance().ensureSpellCheckingLanguage(); 95#endif 96 97 m_callbackForMessageFromInjectedBundle.callback = nullptr; 98 m_callbackForMessageFromInjectedBundle.userData = nullptr; 99} 100 101EwkContext::~EwkContext() 102{ 103 ASSERT(contextMap().get(m_context.get()) == this); 104 105 if (m_jsGlobalContext) 106 JSGlobalContextRelease(m_jsGlobalContext); 107 108 contextMap().remove(m_context.get()); 109} 110 111PassRefPtr<EwkContext> EwkContext::findOrCreateWrapper(WKContextRef context) 112{ 113 if (contextMap().contains(context)) 114 return contextMap().get(context); 115 116 return adoptRef(new EwkContext(context)); 117} 118 119PassRefPtr<EwkContext> EwkContext::create() 120{ 121 return adoptRef(new EwkContext(adoptWK(WKContextCreate()).get())); 122} 123 124PassRefPtr<EwkContext> EwkContext::create(const String& injectedBundlePath) 125{ 126 if (!fileExists(injectedBundlePath)) 127 return 0; 128 129 WKRetainPtr<WKStringRef> path = adoptWK(toCopiedAPI(injectedBundlePath)); 130 131 return adoptRef(new EwkContext(adoptWK(WKContextCreateWithInjectedBundlePath(path.get())).get())); 132} 133 134EwkContext* EwkContext::defaultContext() 135{ 136 static EwkContext* defaultInstance = create().leakRef(); 137 138 return defaultInstance; 139} 140 141EwkApplicationCacheManager* EwkContext::applicationCacheManager() 142{ 143 if (!m_applicationCacheManager) 144 m_applicationCacheManager = std::make_unique<EwkApplicationCacheManager>(WKContextGetApplicationCacheManager(m_context.get())); 145 146 return m_applicationCacheManager.get(); 147} 148 149EwkCookieManager* EwkContext::cookieManager() 150{ 151 if (!m_cookieManager) 152 m_cookieManager = std::make_unique<EwkCookieManager>(WKContextGetCookieManager(m_context.get())); 153 154 return m_cookieManager.get(); 155} 156 157EwkDatabaseManager* EwkContext::databaseManager() 158{ 159 return m_databaseManager.get(); 160} 161 162void EwkContext::ensureFaviconDatabase() 163{ 164 if (m_faviconDatabase) 165 return; 166 167 m_faviconDatabase = std::make_unique<EwkFaviconDatabase>(WKContextGetIconDatabase(m_context.get())); 168} 169 170bool EwkContext::setFaviconDatabaseDirectoryPath(const String& databaseDirectory) 171{ 172 ensureFaviconDatabase(); 173 // FIXME: Hole in WK2 API layering must be fixed when C API is available. 174 WebIconDatabase* iconDatabase = toImpl(WKContextGetIconDatabase(m_context.get())); 175 176 // The database path is already open so its path was 177 // already set. 178 if (iconDatabase->isOpen()) 179 return false; 180 181 // If databaseDirectory is empty, we use the default database path for the platform. 182 String databasePath = databaseDirectory.isEmpty() ? toImpl(m_context.get())->iconDatabasePath() : pathByAppendingComponent(databaseDirectory, WebCore::IconDatabase::defaultDatabaseFilename()); 183 toImpl(m_context.get())->setIconDatabasePath(databasePath); 184 185 return true; 186} 187 188EwkFaviconDatabase* EwkContext::faviconDatabase() 189{ 190 ensureFaviconDatabase(); 191 ASSERT(m_faviconDatabase); 192 193 return m_faviconDatabase.get(); 194} 195 196EwkStorageManager* EwkContext::storageManager() const 197{ 198 return m_storageManager.get(); 199} 200 201RequestManagerClientEfl* EwkContext::requestManager() 202{ 203 return m_requestManagerClient.get(); 204} 205 206void EwkContext::addVisitedLink(const String& visitedURL) 207{ 208 WKContextAddVisitedLink(m_context.get(), adoptWK(toCopiedAPI(visitedURL)).get()); 209} 210 211// Ewk_Cache_Model enum validation 212inline WKCacheModel toWKCacheModel(Ewk_Cache_Model cacheModel) 213{ 214 switch (cacheModel) { 215 case EWK_CACHE_MODEL_DOCUMENT_VIEWER: 216 return kWKCacheModelDocumentViewer; 217 case EWK_CACHE_MODEL_DOCUMENT_BROWSER: 218 return kWKCacheModelDocumentBrowser; 219 case EWK_CACHE_MODEL_PRIMARY_WEBBROWSER: 220 return kWKCacheModelPrimaryWebBrowser; 221 } 222 ASSERT_NOT_REACHED(); 223 224 return kWKCacheModelDocumentViewer; 225} 226 227void EwkContext::setCacheModel(Ewk_Cache_Model cacheModel) 228{ 229 WKContextSetCacheModel(m_context.get(), toWKCacheModel(cacheModel)); 230} 231 232Ewk_Cache_Model EwkContext::cacheModel() const 233{ 234 return static_cast<Ewk_Cache_Model>(WKContextGetCacheModel(m_context.get())); 235} 236 237inline WKProcessModel toWKProcessModel(Ewk_Process_Model processModel) 238{ 239 switch (processModel) { 240 case EWK_PROCESS_MODEL_SHARED_SECONDARY: 241 return kWKProcessModelSharedSecondaryProcess; 242 case EWK_PROCESS_MODEL_MULTIPLE_SECONDARY: 243 return kWKProcessModelMultipleSecondaryProcesses; 244 } 245 ASSERT_NOT_REACHED(); 246 247 return kWKProcessModelSharedSecondaryProcess; 248} 249 250void EwkContext::setProcessModel(Ewk_Process_Model processModel) 251{ 252 WKProcessModel newWKProcessModel = toWKProcessModel(processModel); 253 254 if (WKContextGetProcessModel(m_context.get()) == newWKProcessModel) 255 return; 256 257 WKContextSetUsesNetworkProcess(m_context.get(), newWKProcessModel == kWKProcessModelMultipleSecondaryProcesses); 258 WKContextSetProcessModel(m_context.get(), newWKProcessModel); 259} 260 261inline Ewk_Process_Model toEwkProcessModel(WKProcessModel processModel) 262{ 263 switch (processModel) { 264 case kWKProcessModelSharedSecondaryProcess: 265 return EWK_PROCESS_MODEL_SHARED_SECONDARY; 266 case kWKProcessModelMultipleSecondaryProcesses: 267 return EWK_PROCESS_MODEL_MULTIPLE_SECONDARY; 268 } 269 ASSERT_NOT_REACHED(); 270 271 return EWK_PROCESS_MODEL_SHARED_SECONDARY; 272} 273 274Ewk_Process_Model EwkContext::processModel() const 275{ 276 return toEwkProcessModel(WKContextGetProcessModel(m_context.get())); 277} 278 279#if ENABLE(NETSCAPE_PLUGIN_API) 280void EwkContext::setAdditionalPluginPath(const String& path) 281{ 282 // FIXME: Hole in WK2 API layering must be fixed when C API is available. 283 toImpl(m_context.get())->setAdditionalPluginsDirectory(path); 284} 285#endif 286 287void EwkContext::clearResourceCache() 288{ 289 WKResourceCacheManagerClearCacheForAllOrigins(WKContextGetResourceCacheManager(m_context.get()), WKResourceCachesToClearAll); 290} 291 292 293JSGlobalContextRef EwkContext::jsGlobalContext() 294{ 295 if (!m_jsGlobalContext) 296 m_jsGlobalContext = JSGlobalContextCreate(0); 297 298 return m_jsGlobalContext; 299} 300 301Ewk_Application_Cache_Manager* ewk_context_application_cache_manager_get(const Ewk_Context* ewkContext) 302{ 303 EWK_OBJ_GET_IMPL_OR_RETURN(const EwkContext, ewkContext, impl, nullptr); 304 305 return const_cast<EwkContext*>(impl)->applicationCacheManager(); 306} 307 308Ewk_Cookie_Manager* ewk_context_cookie_manager_get(const Ewk_Context* ewkContext) 309{ 310 EWK_OBJ_GET_IMPL_OR_RETURN(const EwkContext, ewkContext, impl, nullptr); 311 312 return const_cast<EwkContext*>(impl)->cookieManager(); 313} 314 315Ewk_Database_Manager* ewk_context_database_manager_get(const Ewk_Context* ewkContext) 316{ 317 EWK_OBJ_GET_IMPL_OR_RETURN(const EwkContext, ewkContext, impl, nullptr); 318 319 return const_cast<EwkContext*>(impl)->databaseManager(); 320} 321 322Eina_Bool ewk_context_favicon_database_directory_set(Ewk_Context* ewkContext, const char* directoryPath) 323{ 324 EWK_OBJ_GET_IMPL_OR_RETURN(EwkContext, ewkContext, impl, false); 325 326 return impl->setFaviconDatabaseDirectoryPath(String::fromUTF8(directoryPath)); 327} 328 329Ewk_Favicon_Database* ewk_context_favicon_database_get(const Ewk_Context* ewkContext) 330{ 331 EWK_OBJ_GET_IMPL_OR_RETURN(const EwkContext, ewkContext, impl, nullptr); 332 333 return const_cast<EwkContext*>(impl)->faviconDatabase(); 334} 335 336Ewk_Storage_Manager* ewk_context_storage_manager_get(const Ewk_Context* ewkContext) 337{ 338 EWK_OBJ_GET_IMPL_OR_RETURN(const EwkContext, ewkContext, impl, nullptr); 339 340 return impl->storageManager(); 341} 342 343DownloadManagerEfl* EwkContext::downloadManager() const 344{ 345 return m_downloadManager.get(); 346} 347 348ContextHistoryClientEfl* EwkContext::historyClient() 349{ 350 return m_historyClient.get(); 351} 352 353static inline EwkContext* toEwkContext(const void* clientInfo) 354{ 355 return static_cast<EwkContext*>(const_cast<void*>(clientInfo)); 356} 357 358void EwkContext::didReceiveMessageFromInjectedBundle(WKContextRef, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo) 359{ 360 toEwkContext(clientInfo)->processReceivedMessageFromInjectedBundle(messageName, messageBody, nullptr); 361} 362 363void EwkContext::didReceiveSynchronousMessageFromInjectedBundle(WKContextRef, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo) 364{ 365 toEwkContext(clientInfo)->processReceivedMessageFromInjectedBundle(messageName, messageBody, returnData); 366} 367 368void EwkContext::setMessageFromInjectedBundleCallback(Ewk_Context_Message_From_Injected_Bundle_Cb callback, void* userData) 369{ 370 m_callbackForMessageFromInjectedBundle.userData = userData; 371 372 if (m_callbackForMessageFromInjectedBundle.callback == callback) 373 return; 374 375 if (!m_callbackForMessageFromInjectedBundle.callback) { 376 WKContextInjectedBundleClientV1 client; 377 memset(&client, 0, sizeof(client)); 378 379 client.base.version = 1; 380 client.base.clientInfo = this; 381 client.didReceiveMessageFromInjectedBundle = didReceiveMessageFromInjectedBundle; 382 client.didReceiveSynchronousMessageFromInjectedBundle = didReceiveSynchronousMessageFromInjectedBundle; 383 384 WKContextSetInjectedBundleClient(m_context.get(), &client.base); 385 } else if (!callback) 386 WKContextSetInjectedBundleClient(m_context.get(), nullptr); 387 388 m_callbackForMessageFromInjectedBundle.callback = callback; 389} 390 391void EwkContext::processReceivedMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData) 392{ 393 if (!m_callbackForMessageFromInjectedBundle.callback) 394 return; 395 396 CString name = toImpl(messageName)->string().utf8(); 397 CString body; 398 if (messageBody && WKStringGetTypeID() == WKGetTypeID(messageBody)) 399 body = toImpl(static_cast<WKStringRef>(messageBody))->string().utf8(); 400 401 if (returnData) { 402 char* returnString = nullptr; 403 m_callbackForMessageFromInjectedBundle.callback(name.data(), body.data(), &returnString, m_callbackForMessageFromInjectedBundle.userData); 404 if (returnString) { 405 *returnData = WKStringCreateWithUTF8CString(returnString); 406 free(returnString); 407 } else 408 *returnData = WKStringCreateWithUTF8CString(""); 409 } else 410 m_callbackForMessageFromInjectedBundle.callback(name.data(), body.data(), nullptr, m_callbackForMessageFromInjectedBundle.userData); 411} 412 413Ewk_TLS_Error_Policy EwkContext::ignoreTLSErrors() const 414{ 415 return toImpl(m_context.get())->ignoreTLSErrors() ? EWK_TLS_ERROR_POLICY_IGNORE : EWK_TLS_ERROR_POLICY_FAIL; 416} 417 418void EwkContext::setIgnoreTLSErrors(Ewk_TLS_Error_Policy TLSErrorPolicy) const 419{ 420 bool isNewPolicy = TLSErrorPolicy == EWK_TLS_ERROR_POLICY_IGNORE; 421 if (toImpl(m_context.get())->ignoreTLSErrors() == isNewPolicy) 422 return; 423 424 toImpl(m_context.get())->setIgnoreTLSErrors(isNewPolicy); 425} 426 427Ewk_Context* ewk_context_default_get() 428{ 429 return EwkContext::defaultContext(); 430} 431 432Ewk_Context* ewk_context_new() 433{ 434 return EwkContext::create().leakRef(); 435} 436 437Ewk_Context* ewk_context_new_with_injected_bundle_path(const char* path) 438{ 439 EINA_SAFETY_ON_NULL_RETURN_VAL(path, nullptr); 440 441 return EwkContext::create(String::fromUTF8(path)).leakRef(); 442} 443 444Eina_Bool ewk_context_url_scheme_register(Ewk_Context* ewkContext, const char* scheme, Ewk_Url_Scheme_Request_Cb callback, void* userData) 445{ 446 EWK_OBJ_GET_IMPL_OR_RETURN(EwkContext, ewkContext, impl, false); 447 EINA_SAFETY_ON_NULL_RETURN_VAL(scheme, false); 448 EINA_SAFETY_ON_NULL_RETURN_VAL(callback, false); 449 450 impl->requestManager()->registerURLSchemeHandler(String::fromUTF8(scheme), callback, userData); 451 452 return true; 453} 454 455void ewk_context_history_callbacks_set(Ewk_Context* ewkContext, Ewk_History_Navigation_Cb navigate, Ewk_History_Client_Redirection_Cb clientRedirect, Ewk_History_Server_Redirection_Cb serverRedirect, Ewk_History_Title_Update_Cb titleUpdate, Ewk_History_Populate_Visited_Links_Cb populateVisitedLinks, void* data) 456{ 457 EWK_OBJ_GET_IMPL_OR_RETURN(EwkContext, ewkContext, impl); 458 459 impl->historyClient()->setCallbacks(navigate, clientRedirect, serverRedirect, titleUpdate, populateVisitedLinks, data); 460} 461 462void ewk_context_visited_link_add(Ewk_Context* ewkContext, const char* visitedURL) 463{ 464 EWK_OBJ_GET_IMPL_OR_RETURN(EwkContext, ewkContext, impl); 465 EINA_SAFETY_ON_NULL_RETURN(visitedURL); 466 467 impl->addVisitedLink(visitedURL); 468} 469 470Eina_Bool ewk_context_cache_model_set(Ewk_Context* ewkContext, Ewk_Cache_Model cacheModel) 471{ 472 EWK_OBJ_GET_IMPL_OR_RETURN(EwkContext, ewkContext, impl, false); 473 474 impl->setCacheModel(cacheModel); 475 476 return true; 477} 478 479Ewk_Cache_Model ewk_context_cache_model_get(const Ewk_Context* ewkContext) 480{ 481 EWK_OBJ_GET_IMPL_OR_RETURN(const EwkContext, ewkContext, impl, EWK_CACHE_MODEL_DOCUMENT_VIEWER); 482 483 return impl->cacheModel(); 484} 485 486Eina_Bool ewk_context_additional_plugin_path_set(Ewk_Context* ewkContext, const char* path) 487{ 488#if ENABLE(NETSCAPE_PLUGIN_API) 489 EWK_OBJ_GET_IMPL_OR_RETURN(EwkContext, ewkContext, impl, false); 490 EINA_SAFETY_ON_NULL_RETURN_VAL(path, false); 491 492 impl->setAdditionalPluginPath(String::fromUTF8(path)); 493 return true; 494#else 495 UNUSED_PARAM(ewkContext); 496 UNUSED_PARAM(path); 497 return false; 498#endif 499} 500 501void ewk_context_resource_cache_clear(Ewk_Context* ewkContext) 502{ 503 EWK_OBJ_GET_IMPL_OR_RETURN(EwkContext, ewkContext, impl); 504 505 impl->clearResourceCache(); 506} 507 508void ewk_context_message_post_to_injected_bundle(Ewk_Context* ewkContext, const char* name, const char* body) 509{ 510 EWK_OBJ_GET_IMPL_OR_RETURN(EwkContext, ewkContext, impl); 511 EINA_SAFETY_ON_NULL_RETURN(name); 512 EINA_SAFETY_ON_NULL_RETURN(body); 513 514 WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString(name)); 515 WKRetainPtr<WKStringRef> messageBody(AdoptWK, WKStringCreateWithUTF8CString(body)); 516 WKContextPostMessageToInjectedBundle(impl->wkContext(), messageName.get(), messageBody.get()); 517} 518 519void ewk_context_message_from_injected_bundle_callback_set(Ewk_Context* ewkContext, Ewk_Context_Message_From_Injected_Bundle_Cb callback, void* userData) 520{ 521 EWK_OBJ_GET_IMPL_OR_RETURN(EwkContext, ewkContext, impl); 522 523 impl->setMessageFromInjectedBundleCallback(callback, userData); 524} 525 526Eina_Bool ewk_context_process_model_set(Ewk_Context* ewkContext, Ewk_Process_Model processModel) 527{ 528#if ENABLE(NETWORK_PROCESS) 529 EWK_OBJ_GET_IMPL_OR_RETURN(EwkContext, ewkContext, impl, false); 530 531 impl->setProcessModel(processModel); 532 533 return true; 534#else 535 UNUSED_PARAM(ewkContext); 536 UNUSED_PARAM(processModel); 537 return false; 538#endif 539} 540 541Ewk_Process_Model ewk_context_process_model_get(const Ewk_Context* ewkContext) 542{ 543#if ENABLE(NETWORK_PROCESS) 544 EWK_OBJ_GET_IMPL_OR_RETURN(const EwkContext, ewkContext, impl, EWK_PROCESS_MODEL_SHARED_SECONDARY); 545 546 return impl->processModel(); 547#else 548 UNUSED_PARAM(ewkContext); 549 return EWK_PROCESS_MODEL_SHARED_SECONDARY; 550#endif 551} 552 553Ewk_TLS_Error_Policy ewk_context_tls_error_policy_get(const Ewk_Context* context) 554{ 555 EWK_OBJ_GET_IMPL_OR_RETURN(const EwkContext, context, impl, EWK_TLS_ERROR_POLICY_FAIL); 556 557 return impl->ignoreTLSErrors(); 558} 559 560void ewk_context_tls_error_policy_set(Ewk_Context* context, Ewk_TLS_Error_Policy tls_error_policy) 561{ 562 EWK_OBJ_GET_IMPL_OR_RETURN(const EwkContext, context, impl); 563 impl->setIgnoreTLSErrors(tls_error_policy); 564} 565 566void ewk_context_preferred_languages_set(Eina_List* languages) 567{ 568 Vector<String> preferredLanguages; 569 if (languages) { 570 Eina_List* l; 571 void* data; 572 EINA_LIST_FOREACH(languages, l, data) 573 preferredLanguages.append(String::fromUTF8(static_cast<char*>(data)).lower().replace("_", "-")); 574 } 575 576 WebCore::overrideUserPreferredLanguages(preferredLanguages); 577 WebCore::languageDidChange(); 578} 579