Home | History | Annotate | Download | only in page
      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