PageGroup.cpp revision ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddb
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 "Settings.h" 37#include "StorageNamespace.h" 38 39#if PLATFORM(CHROMIUM) 40#include "PlatformBridge.h" 41#endif 42 43#ifdef ANDROID 44#include "DOMWindow.h" 45#include "FileSystem.h" 46#endif 47 48namespace WebCore { 49 50static unsigned getUniqueIdentifier() 51{ 52 static unsigned currentIdentifier = 0; 53 return ++currentIdentifier; 54} 55 56// -------- 57 58static bool shouldTrackVisitedLinks = false; 59 60PageGroup::PageGroup(const String& name) 61 : m_name(name) 62 , m_visitedLinksPopulated(false) 63 , m_identifier(getUniqueIdentifier()) 64 , m_groupSettings(GroupSettings::create()) 65{ 66} 67 68PageGroup::PageGroup(Page* page) 69 : m_visitedLinksPopulated(false) 70 , m_identifier(getUniqueIdentifier()) 71 , m_groupSettings(GroupSettings::create()) 72{ 73 ASSERT(page); 74 addPage(page); 75} 76 77PageGroup::~PageGroup() 78{ 79 removeAllUserContent(); 80} 81 82typedef HashMap<String, PageGroup*> PageGroupMap; 83static PageGroupMap* pageGroups = 0; 84 85PageGroup* PageGroup::pageGroup(const String& groupName) 86{ 87 ASSERT(!groupName.isEmpty()); 88 89 if (!pageGroups) 90 pageGroups = new PageGroupMap; 91 92 pair<PageGroupMap::iterator, bool> result = pageGroups->add(groupName, 0); 93 94 if (result.second) { 95 ASSERT(!result.first->second); 96 result.first->second = new PageGroup(groupName); 97 } 98 99 ASSERT(result.first->second); 100 return result.first->second; 101} 102 103void PageGroup::closeLocalStorage() 104{ 105#if ENABLE(DOM_STORAGE) 106 if (!pageGroups) 107 return; 108 109 PageGroupMap::iterator end = pageGroups->end(); 110 111 for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) { 112 if (it->second->hasLocalStorage()) 113 it->second->localStorage()->close(); 114 } 115#endif 116} 117 118#if ENABLE(DOM_STORAGE) && defined(ANDROID) 119void PageGroup::clearDomStorage() 120{ 121 if (!pageGroups) 122 return; 123 124 125 PageGroupMap::iterator end = pageGroups->end(); 126 127 for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) { 128 String basePath = ""; 129 130 // This is being called as a result of the user explicitly 131 // asking to clear all stored data (e.g. through a settings 132 // dialog. We need a page in the page group to fire a 133 // StorageEvent. There isn't really a correct page to use 134 // as the source (as the clear request hasn't come from a 135 // particular page). One thing we should ensure though is that 136 // we don't try to clear a private browsing mode page as that has no concept 137 // of DOM storage.. 138 139 HashSet<Page*> pages = it->second->pages(); 140 HashSet<Page*>::iterator pagesEnd = pages.end(); 141 Page* page = 0; 142 for(HashSet<Page*>::iterator pit = pages.begin(); pit != pagesEnd; ++pit) { 143 Page* p = *pit; 144 145 // Grab the storage location from an arbitrary page. This is set 146 // to the same value on all private browsing and "normal" pages, 147 // so we can get it from anything. 148 if (basePath.isEmpty()) 149 basePath = p->settings()->localStorageDatabasePath(); 150 151 // DOM storage is disabled in private browsing pages, so nothing to do if 152 // this is such a page. 153 if (p->settings()->privateBrowsingEnabled()) 154 continue; 155 156 // Clear session storage. 157 StorageNamespace* sessionStorage = p->sessionStorage(false); 158 if (sessionStorage) 159 sessionStorage->clear(p); 160 161 // Save this page so we can clear local storage. 162 page = p; 163 } 164 165 // If page is still null at this point, then the only pages that are 166 // open are private browsing pages. Hence no pages are currently using local 167 // storage, so we don't need a page pointer to send any events and the 168 // clear function will handle a 0 input. 169 it->second->localStorage()->clear(page); 170 it->second->localStorage()->close(); 171 172 // Closing the storage areas will stop the background thread and so 173 // we need to remove the local storage ref here so that next time 174 // we come to a site that uses it the thread will get started again. 175 it->second->removeLocalStorage(); 176 177 // At this point, active local and session storage have been cleared and the 178 // StorageAreas for this PageGroup closed. The final sync will have taken 179 // place. All that is left is to purge the database files. 180 if (!basePath.isEmpty()) { 181 Vector<String> files = listDirectory(basePath, "*.localstorage"); 182 Vector<String>::iterator filesEnd = files.end(); 183 for (Vector<String>::iterator it = files.begin(); it != filesEnd; ++it) 184 deleteFile(*it); 185 } 186 } 187} 188 189void PageGroup::removeLocalStorage() 190{ 191 HashSet<Page*> pages = this->pages(); 192 HashSet<Page*>::iterator pagesEnd = pages.end(); 193 for(HashSet<Page*>::iterator pit = pages.begin(); pit != pagesEnd; ++pit) { 194 Page* p = *pit; 195 for (Frame* frame = p->mainFrame(); frame; frame = frame->tree()->traverseNext()) 196 frame->document()->domWindow()->clearDOMStorage(); 197 } 198 199 m_localStorage = 0; 200} 201#endif 202 203void PageGroup::addPage(Page* page) 204{ 205 ASSERT(page); 206 ASSERT(!m_pages.contains(page)); 207 m_pages.add(page); 208} 209 210void PageGroup::removePage(Page* page) 211{ 212 ASSERT(page); 213 ASSERT(m_pages.contains(page)); 214 m_pages.remove(page); 215} 216 217bool PageGroup::isLinkVisited(LinkHash visitedLinkHash) 218{ 219#if PLATFORM(CHROMIUM) 220 // Use Chromium's built-in visited link database. 221 return PlatformBridge::isLinkVisited(visitedLinkHash); 222#else 223 if (!m_visitedLinksPopulated) { 224 m_visitedLinksPopulated = true; 225 ASSERT(!m_pages.isEmpty()); 226 (*m_pages.begin())->chrome()->client()->populateVisitedLinks(); 227 } 228 return m_visitedLinkHashes.contains(visitedLinkHash); 229#endif 230} 231 232void PageGroup::addVisitedLinkHash(LinkHash hash) 233{ 234 if (shouldTrackVisitedLinks) 235 addVisitedLink(hash); 236} 237 238inline void PageGroup::addVisitedLink(LinkHash hash) 239{ 240 ASSERT(shouldTrackVisitedLinks); 241#if !PLATFORM(CHROMIUM) 242 if (!m_visitedLinkHashes.add(hash).second) 243 return; 244#endif 245 Page::visitedStateChanged(this, hash); 246} 247 248void PageGroup::addVisitedLink(const KURL& url) 249{ 250 if (!shouldTrackVisitedLinks) 251 return; 252 ASSERT(!url.isEmpty()); 253 addVisitedLink(visitedLinkHash(url.string().characters(), url.string().length())); 254} 255 256void PageGroup::addVisitedLink(const UChar* characters, size_t length) 257{ 258 if (!shouldTrackVisitedLinks) 259 return; 260 addVisitedLink(visitedLinkHash(characters, length)); 261} 262 263void PageGroup::removeVisitedLinks() 264{ 265 m_visitedLinksPopulated = false; 266 if (m_visitedLinkHashes.isEmpty()) 267 return; 268 m_visitedLinkHashes.clear(); 269 Page::allVisitedStateChanged(this); 270} 271 272void PageGroup::removeAllVisitedLinks() 273{ 274 Page::removeAllVisitedLinks(); 275} 276 277void PageGroup::setShouldTrackVisitedLinks(bool shouldTrack) 278{ 279 if (shouldTrackVisitedLinks == shouldTrack) 280 return; 281 shouldTrackVisitedLinks = shouldTrack; 282 if (!shouldTrackVisitedLinks) 283 removeAllVisitedLinks(); 284} 285 286#if ENABLE(DOM_STORAGE) 287StorageNamespace* PageGroup::localStorage() 288{ 289 if (!m_localStorage) { 290 // Need a page in this page group to query the settings for the local storage database path. 291 // Having these parameters attached to the page settings is unfortunate since these settings are 292 // not per-page (and, in fact, we simply grab the settings from some page at random), but 293 // at this point we're stuck with it. 294 Page* page = *m_pages.begin(); 295 const String& path = page->settings()->localStorageDatabasePath(); 296 unsigned quota = m_groupSettings->localStorageQuotaBytes(); 297 m_localStorage = StorageNamespace::localStorageNamespace(path, quota); 298 } 299 300 return m_localStorage.get(); 301} 302#endif 303 304#if ENABLE(INDEXED_DATABASE) 305IDBFactoryBackendInterface* PageGroup::idbFactory() 306{ 307 // Do not add page setting based access control here since this object is shared by all pages in 308 // the group and having per-page controls is misleading. 309 if (!m_factoryBackend) 310 m_factoryBackend = IDBFactoryBackendInterface::create(); 311 return m_factoryBackend.get(); 312} 313#endif 314 315void PageGroup::addUserScriptToWorld(DOMWrapperWorld* world, const String& source, const KURL& url, 316 PassOwnPtr<Vector<String> > whitelist, PassOwnPtr<Vector<String> > blacklist, 317 UserScriptInjectionTime injectionTime, UserContentInjectedFrames injectedFrames) 318{ 319 ASSERT_ARG(world, world); 320 321 OwnPtr<UserScript> userScript(new UserScript(source, url, whitelist, blacklist, injectionTime, injectedFrames)); 322 if (!m_userScripts) 323 m_userScripts.set(new UserScriptMap); 324 UserScriptVector*& scriptsInWorld = m_userScripts->add(world, 0).first->second; 325 if (!scriptsInWorld) 326 scriptsInWorld = new UserScriptVector; 327 scriptsInWorld->append(userScript.release()); 328} 329 330void PageGroup::addUserStyleSheetToWorld(DOMWrapperWorld* world, const String& source, const KURL& url, 331 PassOwnPtr<Vector<String> > whitelist, PassOwnPtr<Vector<String> > blacklist, 332 UserContentInjectedFrames injectedFrames, 333 UserStyleLevel level, 334 UserStyleInjectionTime injectionTime) 335{ 336 ASSERT_ARG(world, world); 337 338 OwnPtr<UserStyleSheet> userStyleSheet(new UserStyleSheet(source, url, whitelist, blacklist, injectedFrames, level)); 339 if (!m_userStyleSheets) 340 m_userStyleSheets.set(new UserStyleSheetMap); 341 UserStyleSheetVector*& styleSheetsInWorld = m_userStyleSheets->add(world, 0).first->second; 342 if (!styleSheetsInWorld) 343 styleSheetsInWorld = new UserStyleSheetVector; 344 styleSheetsInWorld->append(userStyleSheet.release()); 345 346 if (injectionTime == InjectInExistingDocuments) 347 resetUserStyleCacheInAllFrames(); 348} 349 350void PageGroup::removeUserScriptFromWorld(DOMWrapperWorld* world, const KURL& url) 351{ 352 ASSERT_ARG(world, world); 353 354 if (!m_userScripts) 355 return; 356 357 UserScriptMap::iterator it = m_userScripts->find(world); 358 if (it == m_userScripts->end()) 359 return; 360 361 UserScriptVector* scripts = it->second; 362 for (int i = scripts->size() - 1; i >= 0; --i) { 363 if (scripts->at(i)->url() == url) 364 scripts->remove(i); 365 } 366 367 if (!scripts->isEmpty()) 368 return; 369 370 delete it->second; 371 m_userScripts->remove(it); 372} 373 374void PageGroup::removeUserStyleSheetFromWorld(DOMWrapperWorld* world, const KURL& url) 375{ 376 ASSERT_ARG(world, world); 377 378 if (!m_userStyleSheets) 379 return; 380 381 UserStyleSheetMap::iterator it = m_userStyleSheets->find(world); 382 bool sheetsChanged = false; 383 if (it == m_userStyleSheets->end()) 384 return; 385 386 UserStyleSheetVector* stylesheets = it->second; 387 for (int i = stylesheets->size() - 1; i >= 0; --i) { 388 if (stylesheets->at(i)->url() == url) { 389 stylesheets->remove(i); 390 sheetsChanged = true; 391 } 392 } 393 394 if (!sheetsChanged) 395 return; 396 397 if (!stylesheets->isEmpty()) { 398 delete it->second; 399 m_userStyleSheets->remove(it); 400 } 401 402 resetUserStyleCacheInAllFrames(); 403} 404 405void PageGroup::removeUserScriptsFromWorld(DOMWrapperWorld* world) 406{ 407 ASSERT_ARG(world, world); 408 409 if (!m_userScripts) 410 return; 411 412 UserScriptMap::iterator it = m_userScripts->find(world); 413 if (it == m_userScripts->end()) 414 return; 415 416 delete it->second; 417 m_userScripts->remove(it); 418} 419 420void PageGroup::removeUserStyleSheetsFromWorld(DOMWrapperWorld* world) 421{ 422 ASSERT_ARG(world, world); 423 424 if (!m_userStyleSheets) 425 return; 426 427 UserStyleSheetMap::iterator it = m_userStyleSheets->find(world); 428 if (it == m_userStyleSheets->end()) 429 return; 430 431 delete it->second; 432 m_userStyleSheets->remove(it); 433 434 resetUserStyleCacheInAllFrames(); 435} 436 437void PageGroup::removeAllUserContent() 438{ 439 if (m_userScripts) { 440 deleteAllValues(*m_userScripts); 441 m_userScripts.clear(); 442 } 443 444 if (m_userStyleSheets) { 445 deleteAllValues(*m_userStyleSheets); 446 m_userStyleSheets.clear(); 447 resetUserStyleCacheInAllFrames(); 448 } 449} 450 451void PageGroup::resetUserStyleCacheInAllFrames() 452{ 453 // Clear our cached sheets and have them just reparse. 454 HashSet<Page*>::const_iterator end = m_pages.end(); 455 for (HashSet<Page*>::const_iterator it = m_pages.begin(); it != end; ++it) { 456 for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) 457 frame->document()->updatePageGroupUserSheets(); 458 } 459} 460 461} // namespace WebCore 462