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