1 /* 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "PageGroup.h" 28 29 #include "Chrome.h" 30 #include "ChromeClient.h" 31 #include "Document.h" 32 #include "Frame.h" 33 #include "GroupSettings.h" 34 #include "IDBFactoryBackendInterface.h" 35 #include "Page.h" 36 #include "PageCache.h" 37 #include "SecurityOrigin.h" 38 #include "Settings.h" 39 #include "StorageNamespace.h" 40 41 #if PLATFORM(CHROMIUM) 42 #include "PlatformBridge.h" 43 #endif 44 45 #ifdef ANDROID 46 #include "DOMWindow.h" 47 #include "FileSystem.h" 48 #endif 49 50 namespace WebCore { 51 52 static unsigned getUniqueIdentifier() 53 { 54 static unsigned currentIdentifier = 0; 55 return ++currentIdentifier; 56 } 57 58 // -------- 59 60 static bool shouldTrackVisitedLinks = false; 61 62 PageGroup::PageGroup(const String& name) 63 : m_name(name) 64 , m_visitedLinksPopulated(false) 65 , m_identifier(getUniqueIdentifier()) 66 , m_groupSettings(GroupSettings::create()) 67 { 68 } 69 70 PageGroup::PageGroup(Page* page) 71 : m_visitedLinksPopulated(false) 72 , m_identifier(getUniqueIdentifier()) 73 , m_groupSettings(GroupSettings::create()) 74 { 75 ASSERT(page); 76 addPage(page); 77 } 78 79 PageGroup::~PageGroup() 80 { 81 removeAllUserContent(); 82 } 83 84 typedef HashMap<String, PageGroup*> PageGroupMap; 85 static PageGroupMap* pageGroups = 0; 86 87 PageGroup* PageGroup::pageGroup(const String& groupName) 88 { 89 ASSERT(!groupName.isEmpty()); 90 91 if (!pageGroups) 92 pageGroups = new PageGroupMap; 93 94 pair<PageGroupMap::iterator, bool> result = pageGroups->add(groupName, 0); 95 96 if (result.second) { 97 ASSERT(!result.first->second); 98 result.first->second = new PageGroup(groupName); 99 } 100 101 ASSERT(result.first->second); 102 return result.first->second; 103 } 104 105 void PageGroup::closeLocalStorage() 106 { 107 #if ENABLE(DOM_STORAGE) 108 if (!pageGroups) 109 return; 110 111 PageGroupMap::iterator end = pageGroups->end(); 112 113 for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) { 114 if (it->second->hasLocalStorage()) 115 it->second->localStorage()->close(); 116 } 117 #endif 118 } 119 120 #if ENABLE(DOM_STORAGE) 121 122 void PageGroup::clearLocalStorageForAllOrigins() 123 { 124 if (!pageGroups) 125 return; 126 127 PageGroupMap::iterator end = pageGroups->end(); 128 for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) { 129 if (it->second->hasLocalStorage()) 130 it->second->localStorage()->clearAllOriginsForDeletion(); 131 } 132 } 133 134 void PageGroup::clearLocalStorageForOrigin(SecurityOrigin* origin) 135 { 136 if (!pageGroups) 137 return; 138 139 PageGroupMap::iterator end = pageGroups->end(); 140 for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) { 141 if (it->second->hasLocalStorage()) 142 it->second->localStorage()->clearOriginForDeletion(origin); 143 } 144 } 145 146 void PageGroup::syncLocalStorage() 147 { 148 if (!pageGroups) 149 return; 150 151 PageGroupMap::iterator end = pageGroups->end(); 152 for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) { 153 if (it->second->hasLocalStorage()) 154 it->second->localStorage()->sync(); 155 } 156 } 157 158 unsigned PageGroup::numberOfPageGroups() 159 { 160 if (!pageGroups) 161 return 0; 162 163 return pageGroups->size(); 164 } 165 166 #if defined(ANDROID) 167 void PageGroup::clearDomStorage() 168 { 169 if (!pageGroups) 170 return; 171 172 PageGroupMap::iterator end = pageGroups->end(); 173 174 for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) { 175 String basePath = ""; 176 177 // This is being called as a result of the user explicitly 178 // asking to clear all stored data (e.g. through a settings 179 // dialog. We need a page in the page group to fire a 180 // StorageEvent. There isn't really a correct page to use 181 // as the source (as the clear request hasn't come from a 182 // particular page). One thing we should ensure though is that 183 // we don't try to clear a private browsing mode page as that has no concept 184 // of DOM storage.. 185 186 HashSet<Page*> pages = it->second->pages(); 187 HashSet<Page*>::iterator pagesEnd = pages.end(); 188 Page* page = 0; 189 for(HashSet<Page*>::iterator pit = pages.begin(); pit != pagesEnd; ++pit) { 190 Page* p = *pit; 191 192 // Grab the storage location from an arbitrary page. This is set 193 // to the same value on all private browsing and "normal" pages, 194 // so we can get it from anything. 195 if (basePath.isEmpty()) 196 basePath = p->settings()->localStorageDatabasePath(); 197 198 // DOM storage is disabled in private browsing pages, so nothing to do if 199 // this is such a page. 200 if (p->settings()->privateBrowsingEnabled()) 201 continue; 202 203 // Clear session storage. 204 StorageNamespace* sessionStorage = p->sessionStorage(false); 205 if (sessionStorage) 206 sessionStorage->clear(p); 207 208 // Save this page so we can clear local storage. 209 page = p; 210 } 211 212 // If page is still null at this point, then the only pages that are 213 // open are private browsing pages. Hence no pages are currently using local 214 // storage, so we don't need a page pointer to send any events and the 215 // clear function will handle a 0 input. 216 it->second->localStorage()->clear(page); 217 it->second->localStorage()->close(); 218 219 // Closing the storage areas will stop the background thread and so 220 // we need to remove the local storage ref here so that next time 221 // we come to a site that uses it the thread will get started again. 222 it->second->removeLocalStorage(); 223 224 // At this point, active local and session storage have been cleared and the 225 // StorageAreas for this PageGroup closed. The final sync will have taken 226 // place. All that is left is to purge the database files. 227 if (!basePath.isEmpty()) { 228 Vector<String> files = listDirectory(basePath, "*.localstorage"); 229 Vector<String>::iterator filesEnd = files.end(); 230 for (Vector<String>::iterator it = files.begin(); it != filesEnd; ++it) 231 deleteFile(*it); 232 } 233 } 234 } 235 236 void PageGroup::removeLocalStorage() 237 { 238 HashSet<Page*> pages = this->pages(); 239 HashSet<Page*>::iterator pagesEnd = pages.end(); 240 for(HashSet<Page*>::iterator pit = pages.begin(); pit != pagesEnd; ++pit) { 241 Page* p = *pit; 242 for (Frame* frame = p->mainFrame(); frame; frame = frame->tree()->traverseNext()) 243 frame->document()->domWindow()->clearDOMStorage(); 244 } 245 246 m_localStorage = 0; 247 } 248 #endif // PLATFORM(ANDROID) 249 250 #endif 251 252 void PageGroup::addPage(Page* page) 253 { 254 ASSERT(page); 255 ASSERT(!m_pages.contains(page)); 256 m_pages.add(page); 257 } 258 259 void PageGroup::removePage(Page* page) 260 { 261 ASSERT(page); 262 ASSERT(m_pages.contains(page)); 263 m_pages.remove(page); 264 } 265 266 bool PageGroup::isLinkVisited(LinkHash visitedLinkHash) 267 { 268 #if PLATFORM(CHROMIUM) 269 // Use Chromium's built-in visited link database. 270 return PlatformBridge::isLinkVisited(visitedLinkHash); 271 #else 272 if (!m_visitedLinksPopulated) { 273 m_visitedLinksPopulated = true; 274 ASSERT(!m_pages.isEmpty()); 275 (*m_pages.begin())->chrome()->client()->populateVisitedLinks(); 276 } 277 return m_visitedLinkHashes.contains(visitedLinkHash); 278 #endif 279 } 280 281 void PageGroup::addVisitedLinkHash(LinkHash hash) 282 { 283 if (shouldTrackVisitedLinks) 284 addVisitedLink(hash); 285 } 286 287 inline void PageGroup::addVisitedLink(LinkHash hash) 288 { 289 ASSERT(shouldTrackVisitedLinks); 290 #if !PLATFORM(CHROMIUM) 291 if (!m_visitedLinkHashes.add(hash).second) 292 return; 293 #endif 294 Page::visitedStateChanged(this, hash); 295 pageCache()->markPagesForVistedLinkStyleRecalc(); 296 } 297 298 void PageGroup::addVisitedLink(const KURL& url) 299 { 300 if (!shouldTrackVisitedLinks) 301 return; 302 ASSERT(!url.isEmpty()); 303 addVisitedLink(visitedLinkHash(url.string().characters(), url.string().length())); 304 } 305 306 void PageGroup::addVisitedLink(const UChar* characters, size_t length) 307 { 308 if (!shouldTrackVisitedLinks) 309 return; 310 addVisitedLink(visitedLinkHash(characters, length)); 311 } 312 313 void PageGroup::removeVisitedLinks() 314 { 315 m_visitedLinksPopulated = false; 316 if (m_visitedLinkHashes.isEmpty()) 317 return; 318 m_visitedLinkHashes.clear(); 319 Page::allVisitedStateChanged(this); 320 pageCache()->markPagesForVistedLinkStyleRecalc(); 321 } 322 323 void PageGroup::removeAllVisitedLinks() 324 { 325 Page::removeAllVisitedLinks(); 326 pageCache()->markPagesForVistedLinkStyleRecalc(); 327 } 328 329 void PageGroup::setShouldTrackVisitedLinks(bool shouldTrack) 330 { 331 if (shouldTrackVisitedLinks == shouldTrack) 332 return; 333 shouldTrackVisitedLinks = shouldTrack; 334 if (!shouldTrackVisitedLinks) 335 removeAllVisitedLinks(); 336 } 337 338 #if ENABLE(DOM_STORAGE) 339 StorageNamespace* PageGroup::localStorage() 340 { 341 if (!m_localStorage) { 342 // Need a page in this page group to query the settings for the local storage database path. 343 // Having these parameters attached to the page settings is unfortunate since these settings are 344 // not per-page (and, in fact, we simply grab the settings from some page at random), but 345 // at this point we're stuck with it. 346 Page* page = *m_pages.begin(); 347 const String& path = page->settings()->localStorageDatabasePath(); 348 unsigned quota = m_groupSettings->localStorageQuotaBytes(); 349 m_localStorage = StorageNamespace::localStorageNamespace(path, quota); 350 } 351 352 return m_localStorage.get(); 353 } 354 355 #endif 356 357 #if ENABLE(INDEXED_DATABASE) 358 IDBFactoryBackendInterface* PageGroup::idbFactory() 359 { 360 // Do not add page setting based access control here since this object is shared by all pages in 361 // the group and having per-page controls is misleading. 362 if (!m_factoryBackend) 363 m_factoryBackend = IDBFactoryBackendInterface::create(); 364 return m_factoryBackend.get(); 365 } 366 #endif 367 368 void PageGroup::addUserScriptToWorld(DOMWrapperWorld* world, const String& source, const KURL& url, 369 PassOwnPtr<Vector<String> > whitelist, PassOwnPtr<Vector<String> > blacklist, 370 UserScriptInjectionTime injectionTime, UserContentInjectedFrames injectedFrames) 371 { 372 ASSERT_ARG(world, world); 373 374 OwnPtr<UserScript> userScript(new UserScript(source, url, whitelist, blacklist, injectionTime, injectedFrames)); 375 if (!m_userScripts) 376 m_userScripts.set(new UserScriptMap); 377 UserScriptVector*& scriptsInWorld = m_userScripts->add(world, 0).first->second; 378 if (!scriptsInWorld) 379 scriptsInWorld = new UserScriptVector; 380 scriptsInWorld->append(userScript.release()); 381 } 382 383 void PageGroup::addUserStyleSheetToWorld(DOMWrapperWorld* world, const String& source, const KURL& url, 384 PassOwnPtr<Vector<String> > whitelist, PassOwnPtr<Vector<String> > blacklist, 385 UserContentInjectedFrames injectedFrames, 386 UserStyleLevel level, 387 UserStyleInjectionTime injectionTime) 388 { 389 ASSERT_ARG(world, world); 390 391 OwnPtr<UserStyleSheet> userStyleSheet(new UserStyleSheet(source, url, whitelist, blacklist, injectedFrames, level)); 392 if (!m_userStyleSheets) 393 m_userStyleSheets.set(new UserStyleSheetMap); 394 UserStyleSheetVector*& styleSheetsInWorld = m_userStyleSheets->add(world, 0).first->second; 395 if (!styleSheetsInWorld) 396 styleSheetsInWorld = new UserStyleSheetVector; 397 styleSheetsInWorld->append(userStyleSheet.release()); 398 399 if (injectionTime == InjectInExistingDocuments) 400 resetUserStyleCacheInAllFrames(); 401 } 402 403 void PageGroup::removeUserScriptFromWorld(DOMWrapperWorld* world, const KURL& url) 404 { 405 ASSERT_ARG(world, world); 406 407 if (!m_userScripts) 408 return; 409 410 UserScriptMap::iterator it = m_userScripts->find(world); 411 if (it == m_userScripts->end()) 412 return; 413 414 UserScriptVector* scripts = it->second; 415 for (int i = scripts->size() - 1; i >= 0; --i) { 416 if (scripts->at(i)->url() == url) 417 scripts->remove(i); 418 } 419 420 if (!scripts->isEmpty()) 421 return; 422 423 delete it->second; 424 m_userScripts->remove(it); 425 } 426 427 void PageGroup::removeUserStyleSheetFromWorld(DOMWrapperWorld* world, const KURL& url) 428 { 429 ASSERT_ARG(world, world); 430 431 if (!m_userStyleSheets) 432 return; 433 434 UserStyleSheetMap::iterator it = m_userStyleSheets->find(world); 435 bool sheetsChanged = false; 436 if (it == m_userStyleSheets->end()) 437 return; 438 439 UserStyleSheetVector* stylesheets = it->second; 440 for (int i = stylesheets->size() - 1; i >= 0; --i) { 441 if (stylesheets->at(i)->url() == url) { 442 stylesheets->remove(i); 443 sheetsChanged = true; 444 } 445 } 446 447 if (!sheetsChanged) 448 return; 449 450 if (!stylesheets->isEmpty()) { 451 delete it->second; 452 m_userStyleSheets->remove(it); 453 } 454 455 resetUserStyleCacheInAllFrames(); 456 } 457 458 void PageGroup::removeUserScriptsFromWorld(DOMWrapperWorld* world) 459 { 460 ASSERT_ARG(world, world); 461 462 if (!m_userScripts) 463 return; 464 465 UserScriptMap::iterator it = m_userScripts->find(world); 466 if (it == m_userScripts->end()) 467 return; 468 469 delete it->second; 470 m_userScripts->remove(it); 471 } 472 473 void PageGroup::removeUserStyleSheetsFromWorld(DOMWrapperWorld* world) 474 { 475 ASSERT_ARG(world, world); 476 477 if (!m_userStyleSheets) 478 return; 479 480 UserStyleSheetMap::iterator it = m_userStyleSheets->find(world); 481 if (it == m_userStyleSheets->end()) 482 return; 483 484 delete it->second; 485 m_userStyleSheets->remove(it); 486 487 resetUserStyleCacheInAllFrames(); 488 } 489 490 void PageGroup::removeAllUserContent() 491 { 492 if (m_userScripts) { 493 deleteAllValues(*m_userScripts); 494 m_userScripts.clear(); 495 } 496 497 if (m_userStyleSheets) { 498 deleteAllValues(*m_userStyleSheets); 499 m_userStyleSheets.clear(); 500 resetUserStyleCacheInAllFrames(); 501 } 502 } 503 504 void PageGroup::resetUserStyleCacheInAllFrames() 505 { 506 // Clear our cached sheets and have them just reparse. 507 HashSet<Page*>::const_iterator end = m_pages.end(); 508 for (HashSet<Page*>::const_iterator it = m_pages.begin(); it != end; ++it) { 509 for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) 510 frame->document()->updatePageGroupUserSheets(); 511 } 512 } 513 514 } // namespace WebCore 515