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