Make sure the removal of user stylesheets results in all of the WebViews being updated to
[WebKit.git] / 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 "ChromeClient.h"
30 #include "Document.h"
31 #include "Frame.h"
32 #include "Page.h"
33 #include "Settings.h"
34
35 #if ENABLE(DOM_STORAGE)
36 #include "StorageNamespace.h"
37 #endif
38
39 #if PLATFORM(CHROMIUM)
40 #include "ChromiumBridge.h"
41 #endif
42
43 namespace WebCore {
44
45 static unsigned getUniqueIdentifier()
46 {
47     static unsigned currentIdentifier = 0;
48     return ++currentIdentifier;
49 }
50
51 // --------
52
53 static bool shouldTrackVisitedLinks;
54
55 PageGroup::PageGroup(const String& name)
56     : m_name(name)
57     , m_visitedLinksPopulated(false)
58     , m_identifier(getUniqueIdentifier())
59 {
60 }
61
62 PageGroup::PageGroup(Page* page)
63     : m_visitedLinksPopulated(false)
64     , m_identifier(getUniqueIdentifier())
65 {
66     ASSERT(page);
67     addPage(page);
68 }
69
70 PageGroup::~PageGroup()
71 {
72     removeAllUserContent();
73 }
74
75 typedef HashMap<String, PageGroup*> PageGroupMap;
76 static PageGroupMap* pageGroups = 0;
77
78 PageGroup* PageGroup::pageGroup(const String& groupName)
79 {
80     ASSERT(!groupName.isEmpty());
81     
82     if (!pageGroups)
83         pageGroups = new PageGroupMap;
84
85     pair<PageGroupMap::iterator, bool> result = pageGroups->add(groupName, 0);
86
87     if (result.second) {
88         ASSERT(!result.first->second);
89         result.first->second = new PageGroup(groupName);
90     }
91
92     ASSERT(result.first->second);
93     return result.first->second;
94 }
95
96 void PageGroup::closeLocalStorage()
97 {
98 #if ENABLE(DOM_STORAGE)
99     if (!pageGroups)
100         return;
101
102     PageGroupMap::iterator end = pageGroups->end();
103
104     for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) {
105         if (it->second->hasLocalStorage())
106             it->second->localStorage()->close();
107     }
108 #endif
109 }
110
111 void PageGroup::addPage(Page* page)
112 {
113     ASSERT(page);
114     ASSERT(!m_pages.contains(page));
115     m_pages.add(page);
116 }
117
118 void PageGroup::removePage(Page* page)
119 {
120     ASSERT(page);
121     ASSERT(m_pages.contains(page));
122     m_pages.remove(page);
123 }
124
125 bool PageGroup::isLinkVisited(LinkHash visitedLinkHash)
126 {
127 #if PLATFORM(CHROMIUM)
128     // Use Chromium's built-in visited link database.
129     return ChromiumBridge::isLinkVisited(visitedLinkHash);
130 #else
131     if (!m_visitedLinksPopulated) {
132         m_visitedLinksPopulated = true;
133         ASSERT(!m_pages.isEmpty());
134         (*m_pages.begin())->chrome()->client()->populateVisitedLinks();
135     }
136     return m_visitedLinkHashes.contains(visitedLinkHash);
137 #endif
138 }
139
140 inline void PageGroup::addVisitedLink(LinkHash hash)
141 {
142     ASSERT(shouldTrackVisitedLinks);
143 #if !PLATFORM(CHROMIUM)
144     if (!m_visitedLinkHashes.add(hash).second)
145         return;
146 #endif
147     Page::visitedStateChanged(this, hash);
148 }
149
150 void PageGroup::addVisitedLink(const KURL& url)
151 {
152     if (!shouldTrackVisitedLinks)
153         return;
154     ASSERT(!url.isEmpty());
155     addVisitedLink(visitedLinkHash(url.string().characters(), url.string().length()));
156 }
157
158 void PageGroup::addVisitedLink(const UChar* characters, size_t length)
159 {
160     if (!shouldTrackVisitedLinks)
161         return;
162     addVisitedLink(visitedLinkHash(characters, length));
163 }
164
165 void PageGroup::removeVisitedLinks()
166 {
167     m_visitedLinksPopulated = false;
168     if (m_visitedLinkHashes.isEmpty())
169         return;
170     m_visitedLinkHashes.clear();
171     Page::allVisitedStateChanged(this);
172 }
173
174 void PageGroup::removeAllVisitedLinks()
175 {
176     Page::removeAllVisitedLinks();
177 }
178
179 void PageGroup::setShouldTrackVisitedLinks(bool shouldTrack)
180 {
181     if (shouldTrackVisitedLinks == shouldTrack)
182         return;
183     shouldTrackVisitedLinks = shouldTrack;
184     if (!shouldTrackVisitedLinks)
185         removeAllVisitedLinks();
186 }
187
188 #if ENABLE(DOM_STORAGE)
189 StorageNamespace* PageGroup::localStorage()
190 {
191     if (!m_localStorage) {
192         // Need a page in this page group to query the settings for the local storage database path.
193         Page* page = *m_pages.begin();
194         ASSERT(page);
195         m_localStorage = StorageNamespace::localStorageNamespace(page->settings()->localStorageDatabasePath());
196     }
197
198     return m_localStorage.get();
199 }
200 #endif
201
202 void PageGroup::addUserScript(const String& source, const KURL& url, const Vector<String>& patterns, 
203                               unsigned worldID, UserScriptInjectionTime injectionTime)
204 {
205     if (worldID == UINT_MAX)
206         return;
207     OwnPtr<UserScript> userScript(new UserScript(source, url, patterns, worldID, injectionTime));
208     if (!m_userScripts)
209         m_userScripts.set(new UserScriptMap);
210     UserScriptVector*& scriptsInWorld = m_userScripts->add(worldID, 0).first->second;
211     if (!scriptsInWorld)
212         scriptsInWorld = new UserScriptVector;
213     scriptsInWorld->append(userScript.release());
214 }
215
216 void PageGroup::addUserStyleSheet(const String& source, const KURL& url, const Vector<String>& patterns, unsigned worldID)
217 {
218     if (worldID == UINT_MAX)
219         return;
220     OwnPtr<UserStyleSheet> userStyleSheet(new UserStyleSheet(source, url, patterns, worldID));
221     if (!m_userStyleSheets)
222         m_userStyleSheets.set(new UserStyleSheetMap);
223     UserStyleSheetVector*& styleSheetsInWorld = m_userStyleSheets->add(worldID, 0).first->second;
224     if (!styleSheetsInWorld)
225         styleSheetsInWorld = new UserStyleSheetVector;
226     styleSheetsInWorld->append(userStyleSheet.release());
227     
228     // Clear our cached sheets and have them just reparse.
229     HashSet<Page*>::const_iterator end = m_pages.end();
230     for (HashSet<Page*>::const_iterator it = m_pages.begin(); it != end; ++it) {
231         for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext())
232             frame->document()->clearPageGroupUserSheets();
233     }
234 }
235
236 void PageGroup::removeUserContentWithURLForWorld(const KURL& url, unsigned worldID)
237 {
238     if (m_userScripts) {
239         UserScriptMap::iterator it = m_userScripts->find(worldID);
240         if (it != m_userScripts->end()) {
241             UserScriptVector* scripts = it->second;
242             for (int i = scripts->size() - 1; i >= 0; --i) {
243                 if (scripts->at(i)->url() == url)
244                     scripts->remove(i);
245             }
246             
247             if (scripts->isEmpty()) {
248                 m_userScripts->remove(it);
249                 delete it->second;
250             }
251         }
252     }
253     
254     if (m_userStyleSheets) {
255         UserStyleSheetMap::iterator it = m_userStyleSheets->find(worldID);
256         bool sheetsChanged = false;
257         if (it != m_userStyleSheets->end()) {
258             UserStyleSheetVector* stylesheets = it->second;
259             for (int i = stylesheets->size() - 1; i >= 0; --i) {
260                 if (stylesheets->at(i)->url() == url) {
261                     stylesheets->remove(i);
262                     sheetsChanged = true;
263                 }
264             }
265             
266             if (stylesheets->isEmpty()) {
267                 m_userStyleSheets->remove(it);
268                 delete it->second;
269             }
270         }
271         
272         // Clear our cached sheets and have them just reparse.
273         if (sheetsChanged) {
274             HashSet<Page*>::const_iterator end = m_pages.end();
275             for (HashSet<Page*>::const_iterator it = m_pages.begin(); it != end; ++it) {
276                 for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext())
277                     frame->document()->clearPageGroupUserSheets();
278             }
279         }
280     }
281 }
282
283 void PageGroup::removeUserContentForWorld(unsigned worldID)
284 {
285     if (m_userScripts) {
286         UserScriptMap::iterator it = m_userScripts->find(worldID);
287         if (it != m_userScripts->end()) {
288             m_userScripts->remove(it);
289             delete it->second;
290         }
291     }
292     
293     if (m_userStyleSheets) {
294         bool sheetsChanged = false;
295         UserStyleSheetMap::iterator it = m_userStyleSheets->find(worldID);
296         if (it != m_userStyleSheets->end()) {
297             m_userStyleSheets->remove(it);
298             sheetsChanged = true;
299             delete it->second;
300         }
301     
302         if (sheetsChanged) {
303             // Clear our cached sheets and have them just reparse.
304             HashSet<Page*>::const_iterator end = m_pages.end();
305             for (HashSet<Page*>::const_iterator it = m_pages.begin(); it != end; ++it) {
306                 for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext())
307                     frame->document()->clearPageGroupUserSheets();
308             }
309         }
310     }
311 }
312
313 void PageGroup::removeAllUserContent()
314 {
315     if (m_userScripts) {
316         deleteAllValues(*m_userScripts);
317         m_userScripts.clear();
318     }
319     
320     
321     if (m_userStyleSheets) {
322         deleteAllValues(*m_userStyleSheets);
323         m_userStyleSheets.clear();
324     }
325 }
326
327 } // namespace WebCore