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 namespace WebCore {
46 
getUniqueIdentifier()47 static unsigned getUniqueIdentifier()
48 {
49     static unsigned currentIdentifier = 0;
50     return ++currentIdentifier;
51 }
52 
53 // --------
54 
55 static bool shouldTrackVisitedLinks = false;
56 
PageGroup(const String & name)57 PageGroup::PageGroup(const String& name)
58     : m_name(name)
59     , m_visitedLinksPopulated(false)
60     , m_identifier(getUniqueIdentifier())
61     , m_groupSettings(GroupSettings::create())
62 {
63 }
64 
PageGroup(Page * page)65 PageGroup::PageGroup(Page* page)
66     : m_visitedLinksPopulated(false)
67     , m_identifier(getUniqueIdentifier())
68     , m_groupSettings(GroupSettings::create())
69 {
70     ASSERT(page);
71     addPage(page);
72 }
73 
~PageGroup()74 PageGroup::~PageGroup()
75 {
76     removeAllUserContent();
77 }
78 
79 typedef HashMap<String, PageGroup*> PageGroupMap;
80 static PageGroupMap* pageGroups = 0;
81 
pageGroup(const String & groupName)82 PageGroup* PageGroup::pageGroup(const String& groupName)
83 {
84     ASSERT(!groupName.isEmpty());
85 
86     if (!pageGroups)
87         pageGroups = new PageGroupMap;
88 
89     pair<PageGroupMap::iterator, bool> result = pageGroups->add(groupName, 0);
90 
91     if (result.second) {
92         ASSERT(!result.first->second);
93         result.first->second = new PageGroup(groupName);
94     }
95 
96     ASSERT(result.first->second);
97     return result.first->second;
98 }
99 
closeLocalStorage()100 void PageGroup::closeLocalStorage()
101 {
102 #if ENABLE(DOM_STORAGE)
103     if (!pageGroups)
104         return;
105 
106     PageGroupMap::iterator end = pageGroups->end();
107 
108     for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) {
109         if (it->second->hasLocalStorage())
110             it->second->localStorage()->close();
111     }
112 #endif
113 }
114 
115 #if ENABLE(DOM_STORAGE)
116 
clearLocalStorageForAllOrigins()117 void PageGroup::clearLocalStorageForAllOrigins()
118 {
119     if (!pageGroups)
120         return;
121 
122     PageGroupMap::iterator end = pageGroups->end();
123     for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) {
124         if (it->second->hasLocalStorage())
125             it->second->localStorage()->clearAllOriginsForDeletion();
126     }
127 }
128 
clearLocalStorageForOrigin(SecurityOrigin * origin)129 void PageGroup::clearLocalStorageForOrigin(SecurityOrigin* origin)
130 {
131     if (!pageGroups)
132         return;
133 
134     PageGroupMap::iterator end = pageGroups->end();
135     for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) {
136         if (it->second->hasLocalStorage())
137             it->second->localStorage()->clearOriginForDeletion(origin);
138     }
139 }
140 
syncLocalStorage()141 void PageGroup::syncLocalStorage()
142 {
143     if (!pageGroups)
144         return;
145 
146     PageGroupMap::iterator end = pageGroups->end();
147     for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) {
148         if (it->second->hasLocalStorage())
149             it->second->localStorage()->sync();
150     }
151 }
152 
numberOfPageGroups()153 unsigned PageGroup::numberOfPageGroups()
154 {
155     if (!pageGroups)
156         return 0;
157 
158     return pageGroups->size();
159 }
160 
161 #endif
162 
addPage(Page * page)163 void PageGroup::addPage(Page* page)
164 {
165     ASSERT(page);
166     ASSERT(!m_pages.contains(page));
167     m_pages.add(page);
168 }
169 
removePage(Page * page)170 void PageGroup::removePage(Page* page)
171 {
172     ASSERT(page);
173     ASSERT(m_pages.contains(page));
174     m_pages.remove(page);
175 }
176 
isLinkVisited(LinkHash visitedLinkHash)177 bool PageGroup::isLinkVisited(LinkHash visitedLinkHash)
178 {
179 #if PLATFORM(CHROMIUM)
180     // Use Chromium's built-in visited link database.
181     return PlatformBridge::isLinkVisited(visitedLinkHash);
182 #else
183     if (!m_visitedLinksPopulated) {
184         m_visitedLinksPopulated = true;
185         ASSERT(!m_pages.isEmpty());
186         (*m_pages.begin())->chrome()->client()->populateVisitedLinks();
187     }
188     return m_visitedLinkHashes.contains(visitedLinkHash);
189 #endif
190 }
191 
addVisitedLinkHash(LinkHash hash)192 void PageGroup::addVisitedLinkHash(LinkHash hash)
193 {
194     if (shouldTrackVisitedLinks)
195         addVisitedLink(hash);
196 }
197 
addVisitedLink(LinkHash hash)198 inline void PageGroup::addVisitedLink(LinkHash hash)
199 {
200     ASSERT(shouldTrackVisitedLinks);
201 #if !PLATFORM(CHROMIUM)
202     if (!m_visitedLinkHashes.add(hash).second)
203         return;
204 #endif
205     Page::visitedStateChanged(this, hash);
206     pageCache()->markPagesForVistedLinkStyleRecalc();
207 }
208 
addVisitedLink(const KURL & url)209 void PageGroup::addVisitedLink(const KURL& url)
210 {
211     if (!shouldTrackVisitedLinks)
212         return;
213     ASSERT(!url.isEmpty());
214     addVisitedLink(visitedLinkHash(url.string().characters(), url.string().length()));
215 }
216 
addVisitedLink(const UChar * characters,size_t length)217 void PageGroup::addVisitedLink(const UChar* characters, size_t length)
218 {
219     if (!shouldTrackVisitedLinks)
220         return;
221     addVisitedLink(visitedLinkHash(characters, length));
222 }
223 
removeVisitedLinks()224 void PageGroup::removeVisitedLinks()
225 {
226     m_visitedLinksPopulated = false;
227     if (m_visitedLinkHashes.isEmpty())
228         return;
229     m_visitedLinkHashes.clear();
230     Page::allVisitedStateChanged(this);
231     pageCache()->markPagesForVistedLinkStyleRecalc();
232 }
233 
removeAllVisitedLinks()234 void PageGroup::removeAllVisitedLinks()
235 {
236     Page::removeAllVisitedLinks();
237     pageCache()->markPagesForVistedLinkStyleRecalc();
238 }
239 
setShouldTrackVisitedLinks(bool shouldTrack)240 void PageGroup::setShouldTrackVisitedLinks(bool shouldTrack)
241 {
242     if (shouldTrackVisitedLinks == shouldTrack)
243         return;
244     shouldTrackVisitedLinks = shouldTrack;
245     if (!shouldTrackVisitedLinks)
246         removeAllVisitedLinks();
247 }
248 
249 #if ENABLE(DOM_STORAGE)
localStorage()250 StorageNamespace* PageGroup::localStorage()
251 {
252     if (!m_localStorage) {
253         // Need a page in this page group to query the settings for the local storage database path.
254         // Having these parameters attached to the page settings is unfortunate since these settings are
255         // not per-page (and, in fact, we simply grab the settings from some page at random), but
256         // at this point we're stuck with it.
257         Page* page = *m_pages.begin();
258         const String& path = page->settings()->localStorageDatabasePath();
259         unsigned quota = m_groupSettings->localStorageQuotaBytes();
260         m_localStorage = StorageNamespace::localStorageNamespace(path, quota);
261     }
262 
263     return m_localStorage.get();
264 }
265 
266 #endif
267 
268 #if ENABLE(INDEXED_DATABASE)
idbFactory()269 IDBFactoryBackendInterface* PageGroup::idbFactory()
270 {
271     // Do not add page setting based access control here since this object is shared by all pages in
272     // the group and having per-page controls is misleading.
273     if (!m_factoryBackend)
274         m_factoryBackend = IDBFactoryBackendInterface::create();
275     return m_factoryBackend.get();
276 }
277 #endif
278 
addUserScriptToWorld(DOMWrapperWorld * world,const String & source,const KURL & url,PassOwnPtr<Vector<String>> whitelist,PassOwnPtr<Vector<String>> blacklist,UserScriptInjectionTime injectionTime,UserContentInjectedFrames injectedFrames)279 void PageGroup::addUserScriptToWorld(DOMWrapperWorld* world, const String& source, const KURL& url,
280                                      PassOwnPtr<Vector<String> > whitelist, PassOwnPtr<Vector<String> > blacklist,
281                                      UserScriptInjectionTime injectionTime, UserContentInjectedFrames injectedFrames)
282 {
283     ASSERT_ARG(world, world);
284 
285     OwnPtr<UserScript> userScript = adoptPtr(new UserScript(source, url, whitelist, blacklist, injectionTime, injectedFrames));
286     if (!m_userScripts)
287         m_userScripts = adoptPtr(new UserScriptMap);
288     UserScriptVector*& scriptsInWorld = m_userScripts->add(world, 0).first->second;
289     if (!scriptsInWorld)
290         scriptsInWorld = new UserScriptVector;
291     scriptsInWorld->append(userScript.release());
292 }
293 
addUserStyleSheetToWorld(DOMWrapperWorld * world,const String & source,const KURL & url,PassOwnPtr<Vector<String>> whitelist,PassOwnPtr<Vector<String>> blacklist,UserContentInjectedFrames injectedFrames,UserStyleLevel level,UserStyleInjectionTime injectionTime)294 void PageGroup::addUserStyleSheetToWorld(DOMWrapperWorld* world, const String& source, const KURL& url,
295                                          PassOwnPtr<Vector<String> > whitelist, PassOwnPtr<Vector<String> > blacklist,
296                                          UserContentInjectedFrames injectedFrames,
297                                          UserStyleLevel level,
298                                          UserStyleInjectionTime injectionTime)
299 {
300     ASSERT_ARG(world, world);
301 
302     OwnPtr<UserStyleSheet> userStyleSheet = adoptPtr(new UserStyleSheet(source, url, whitelist, blacklist, injectedFrames, level));
303     if (!m_userStyleSheets)
304         m_userStyleSheets = adoptPtr(new UserStyleSheetMap);
305     UserStyleSheetVector*& styleSheetsInWorld = m_userStyleSheets->add(world, 0).first->second;
306     if (!styleSheetsInWorld)
307         styleSheetsInWorld = new UserStyleSheetVector;
308     styleSheetsInWorld->append(userStyleSheet.release());
309 
310     if (injectionTime == InjectInExistingDocuments)
311         resetUserStyleCacheInAllFrames();
312 }
313 
removeUserScriptFromWorld(DOMWrapperWorld * world,const KURL & url)314 void PageGroup::removeUserScriptFromWorld(DOMWrapperWorld* world, const KURL& url)
315 {
316     ASSERT_ARG(world, world);
317 
318     if (!m_userScripts)
319         return;
320 
321     UserScriptMap::iterator it = m_userScripts->find(world);
322     if (it == m_userScripts->end())
323         return;
324 
325     UserScriptVector* scripts = it->second;
326     for (int i = scripts->size() - 1; i >= 0; --i) {
327         if (scripts->at(i)->url() == url)
328             scripts->remove(i);
329     }
330 
331     if (!scripts->isEmpty())
332         return;
333 
334     delete it->second;
335     m_userScripts->remove(it);
336 }
337 
removeUserStyleSheetFromWorld(DOMWrapperWorld * world,const KURL & url)338 void PageGroup::removeUserStyleSheetFromWorld(DOMWrapperWorld* world, const KURL& url)
339 {
340     ASSERT_ARG(world, world);
341 
342     if (!m_userStyleSheets)
343         return;
344 
345     UserStyleSheetMap::iterator it = m_userStyleSheets->find(world);
346     bool sheetsChanged = false;
347     if (it == m_userStyleSheets->end())
348         return;
349 
350     UserStyleSheetVector* stylesheets = it->second;
351     for (int i = stylesheets->size() - 1; i >= 0; --i) {
352         if (stylesheets->at(i)->url() == url) {
353             stylesheets->remove(i);
354             sheetsChanged = true;
355         }
356     }
357 
358     if (!sheetsChanged)
359         return;
360 
361     if (!stylesheets->isEmpty()) {
362         delete it->second;
363         m_userStyleSheets->remove(it);
364     }
365 
366     resetUserStyleCacheInAllFrames();
367 }
368 
removeUserScriptsFromWorld(DOMWrapperWorld * world)369 void PageGroup::removeUserScriptsFromWorld(DOMWrapperWorld* world)
370 {
371     ASSERT_ARG(world, world);
372 
373     if (!m_userScripts)
374         return;
375 
376     UserScriptMap::iterator it = m_userScripts->find(world);
377     if (it == m_userScripts->end())
378         return;
379 
380     delete it->second;
381     m_userScripts->remove(it);
382 }
383 
removeUserStyleSheetsFromWorld(DOMWrapperWorld * world)384 void PageGroup::removeUserStyleSheetsFromWorld(DOMWrapperWorld* world)
385 {
386     ASSERT_ARG(world, world);
387 
388     if (!m_userStyleSheets)
389         return;
390 
391     UserStyleSheetMap::iterator it = m_userStyleSheets->find(world);
392     if (it == m_userStyleSheets->end())
393         return;
394 
395     delete it->second;
396     m_userStyleSheets->remove(it);
397 
398     resetUserStyleCacheInAllFrames();
399 }
400 
removeAllUserContent()401 void PageGroup::removeAllUserContent()
402 {
403     if (m_userScripts) {
404         deleteAllValues(*m_userScripts);
405         m_userScripts.clear();
406     }
407 
408     if (m_userStyleSheets) {
409         deleteAllValues(*m_userStyleSheets);
410         m_userStyleSheets.clear();
411         resetUserStyleCacheInAllFrames();
412     }
413 }
414 
resetUserStyleCacheInAllFrames()415 void PageGroup::resetUserStyleCacheInAllFrames()
416 {
417     // Clear our cached sheets and have them just reparse.
418     HashSet<Page*>::const_iterator end = m_pages.end();
419     for (HashSet<Page*>::const_iterator it = m_pages.begin(); it != end; ++it) {
420         for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext())
421             frame->document()->updatePageGroupUserSheets();
422     }
423 }
424 
425 } // namespace WebCore
426