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