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
getUniqueIdentifier()51 static unsigned getUniqueIdentifier()
52 {
53 static unsigned currentIdentifier = 0;
54 return ++currentIdentifier;
55 }
56
57 // --------
58
59 static bool shouldTrackVisitedLinks = false;
60
PageGroup(const String & name)61 PageGroup::PageGroup(const String& name)
62 : m_name(name)
63 , m_visitedLinksPopulated(false)
64 , m_identifier(getUniqueIdentifier())
65 {
66 }
67
PageGroup(Page * page)68 PageGroup::PageGroup(Page* page)
69 : m_visitedLinksPopulated(false)
70 , m_identifier(getUniqueIdentifier())
71 {
72 ASSERT(page);
73 addPage(page);
74 }
75
~PageGroup()76 PageGroup::~PageGroup()
77 {
78 removeAllUserContent();
79 }
80
81 typedef HashMap<String, PageGroup*> PageGroupMap;
82 static PageGroupMap* pageGroups = 0;
83
pageGroup(const String & groupName)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
closeLocalStorage()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)
clearDomStorage()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
removeLocalStorage()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
addPage(Page * page)202 void PageGroup::addPage(Page* page)
203 {
204 ASSERT(page);
205 ASSERT(!m_pages.contains(page));
206 m_pages.add(page);
207 }
208
removePage(Page * page)209 void PageGroup::removePage(Page* page)
210 {
211 ASSERT(page);
212 ASSERT(m_pages.contains(page));
213 m_pages.remove(page);
214 }
215
isLinkVisited(LinkHash visitedLinkHash)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
addVisitedLink(LinkHash hash)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
addVisitedLink(const KURL & url)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
addVisitedLink(const UChar * characters,size_t length)249 void PageGroup::addVisitedLink(const UChar* characters, size_t length)
250 {
251 if (!shouldTrackVisitedLinks)
252 return;
253 addVisitedLink(visitedLinkHash(characters, length));
254 }
255
removeVisitedLinks()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
removeAllVisitedLinks()265 void PageGroup::removeAllVisitedLinks()
266 {
267 Page::removeAllVisitedLinks();
268 }
269
setShouldTrackVisitedLinks(bool shouldTrack)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)
localStorage()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
addUserScriptToWorld(DOMWrapperWorld * world,const String & source,const KURL & url,PassOwnPtr<Vector<String>> whitelist,PassOwnPtr<Vector<String>> blacklist,UserScriptInjectionTime injectionTime)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
addUserStyleSheetToWorld(DOMWrapperWorld * world,const String & source,const KURL & url,PassOwnPtr<Vector<String>> whitelist,PassOwnPtr<Vector<String>> blacklist)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
removeUserScriptFromWorld(DOMWrapperWorld * world,const KURL & url)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
removeUserStyleSheetFromWorld(DOMWrapperWorld * world,const KURL & url)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
removeUserScriptsFromWorld(DOMWrapperWorld * world)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
removeUserStyleSheetsFromWorld(DOMWrapperWorld * world)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
removeAllUserContent()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