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 "DOMWrapperWorld.h"
32#include "Document.h"
33#include "DocumentStyleSheetCollection.h"
34#include "Frame.h"
35#include "GroupSettings.h"
36#include "Page.h"
37#include "PageCache.h"
38#include "SecurityOrigin.h"
39#include "Settings.h"
40#include "StorageNamespace.h"
41
42#if ENABLE(VIDEO_TRACK)
43#if (PLATFORM(MAC) && !PLATFORM(IOS)) || HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK)
44#include "CaptionUserPreferencesMediaAF.h"
45#else
46#include "CaptionUserPreferences.h"
47#endif
48#endif
49
50namespace WebCore {
51
52static unsigned getUniqueIdentifier()
53{
54    static unsigned currentIdentifier = 0;
55    return ++currentIdentifier;
56}
57
58// --------
59
60static bool shouldTrackVisitedLinks = false;
61
62PageGroup::PageGroup(const String& name)
63    : m_name(name)
64    , m_visitedLinksPopulated(false)
65    , m_identifier(getUniqueIdentifier())
66    , m_groupSettings(GroupSettings::create())
67{
68}
69
70PageGroup::PageGroup(Page* page)
71    : m_visitedLinksPopulated(false)
72    , m_identifier(getUniqueIdentifier())
73    , m_groupSettings(GroupSettings::create())
74{
75    ASSERT(page);
76    addPage(page);
77}
78
79PageGroup::~PageGroup()
80{
81    removeAllUserContent();
82}
83
84PassOwnPtr<PageGroup> PageGroup::create(Page* page)
85{
86    return adoptPtr(new PageGroup(page));
87}
88
89typedef HashMap<String, PageGroup*> PageGroupMap;
90static PageGroupMap* pageGroups = 0;
91
92PageGroup* PageGroup::pageGroup(const String& groupName)
93{
94    ASSERT(!groupName.isEmpty());
95
96    if (!pageGroups)
97        pageGroups = new PageGroupMap;
98
99    PageGroupMap::AddResult result = pageGroups->add(groupName, 0);
100
101    if (result.isNewEntry) {
102        ASSERT(!result.iterator->value);
103        result.iterator->value = new PageGroup(groupName);
104    }
105
106    ASSERT(result.iterator->value);
107    return result.iterator->value;
108}
109
110void PageGroup::closeLocalStorage()
111{
112    if (!pageGroups)
113        return;
114
115    PageGroupMap::iterator end = pageGroups->end();
116
117    for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) {
118        if (it->value->hasLocalStorage())
119            it->value->localStorage()->close();
120    }
121}
122
123void PageGroup::clearLocalStorageForAllOrigins()
124{
125    if (!pageGroups)
126        return;
127
128    PageGroupMap::iterator end = pageGroups->end();
129    for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) {
130        if (it->value->hasLocalStorage())
131            it->value->localStorage()->clearAllOriginsForDeletion();
132    }
133}
134
135void PageGroup::clearLocalStorageForOrigin(SecurityOrigin* origin)
136{
137    if (!pageGroups)
138        return;
139
140    PageGroupMap::iterator end = pageGroups->end();
141    for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) {
142        if (it->value->hasLocalStorage())
143            it->value->localStorage()->clearOriginForDeletion(origin);
144    }
145}
146
147void PageGroup::closeIdleLocalStorageDatabases()
148{
149    if (!pageGroups)
150        return;
151
152    PageGroupMap::iterator end = pageGroups->end();
153    for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) {
154        if (it->value->hasLocalStorage())
155            it->value->localStorage()->closeIdleLocalStorageDatabases();
156    }
157}
158
159void PageGroup::syncLocalStorage()
160{
161    if (!pageGroups)
162        return;
163
164    PageGroupMap::iterator end = pageGroups->end();
165    for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) {
166        if (it->value->hasLocalStorage())
167            it->value->localStorage()->sync();
168    }
169}
170
171unsigned PageGroup::numberOfPageGroups()
172{
173    if (!pageGroups)
174        return 0;
175
176    return pageGroups->size();
177}
178
179void PageGroup::addPage(Page* page)
180{
181    ASSERT(page);
182    ASSERT(!m_pages.contains(page));
183    m_pages.add(page);
184}
185
186void PageGroup::removePage(Page* page)
187{
188    ASSERT(page);
189    ASSERT(m_pages.contains(page));
190    m_pages.remove(page);
191}
192
193bool PageGroup::isLinkVisited(LinkHash visitedLinkHash)
194{
195    if (!m_visitedLinksPopulated) {
196        m_visitedLinksPopulated = true;
197        ASSERT(!m_pages.isEmpty());
198        (*m_pages.begin())->chrome().client()->populateVisitedLinks();
199    }
200    return m_visitedLinkHashes.contains(visitedLinkHash);
201}
202
203void PageGroup::addVisitedLinkHash(LinkHash hash)
204{
205    if (shouldTrackVisitedLinks)
206        addVisitedLink(hash);
207}
208
209inline void PageGroup::addVisitedLink(LinkHash hash)
210{
211    ASSERT(shouldTrackVisitedLinks);
212    if (!m_visitedLinkHashes.add(hash).isNewEntry)
213        return;
214    Page::visitedStateChanged(this, hash);
215    pageCache()->markPagesForVistedLinkStyleRecalc();
216}
217
218void PageGroup::addVisitedLink(const KURL& url)
219{
220    if (!shouldTrackVisitedLinks)
221        return;
222    ASSERT(!url.isEmpty());
223    addVisitedLink(visitedLinkHash(url.string()));
224}
225
226void PageGroup::addVisitedLink(const UChar* characters, size_t length)
227{
228    if (!shouldTrackVisitedLinks)
229        return;
230    addVisitedLink(visitedLinkHash(characters, length));
231}
232
233void PageGroup::removeVisitedLinks()
234{
235    m_visitedLinksPopulated = false;
236    if (m_visitedLinkHashes.isEmpty())
237        return;
238    m_visitedLinkHashes.clear();
239    Page::allVisitedStateChanged(this);
240    pageCache()->markPagesForVistedLinkStyleRecalc();
241}
242
243void PageGroup::removeAllVisitedLinks()
244{
245    Page::removeAllVisitedLinks();
246    pageCache()->markPagesForVistedLinkStyleRecalc();
247}
248
249void PageGroup::setShouldTrackVisitedLinks(bool shouldTrack)
250{
251    if (shouldTrackVisitedLinks == shouldTrack)
252        return;
253    shouldTrackVisitedLinks = shouldTrack;
254    if (!shouldTrackVisitedLinks)
255        removeAllVisitedLinks();
256}
257
258StorageNamespace* PageGroup::localStorage()
259{
260    if (!m_localStorage)
261        m_localStorage = StorageNamespace::localStorageNamespace(this);
262
263    return m_localStorage.get();
264}
265
266StorageNamespace* PageGroup::transientLocalStorage(SecurityOrigin* topOrigin)
267{
268    HashMap<RefPtr<SecurityOrigin>, RefPtr<StorageNamespace> >::AddResult result = m_transientLocalStorageMap.add(topOrigin, 0);
269
270    if (result.isNewEntry)
271        result.iterator->value = StorageNamespace::transientLocalStorageNamespace(this, topOrigin);
272
273    return result.iterator->value.get();
274}
275
276void PageGroup::addUserScriptToWorld(DOMWrapperWorld* world, const String& source, const KURL& url,
277                                     const Vector<String>& whitelist, const Vector<String>& blacklist,
278                                     UserScriptInjectionTime injectionTime, UserContentInjectedFrames injectedFrames)
279{
280    ASSERT_ARG(world, world);
281
282    OwnPtr<UserScript> userScript = adoptPtr(new UserScript(source, url, whitelist, blacklist, injectionTime, injectedFrames));
283    if (!m_userScripts)
284        m_userScripts = adoptPtr(new UserScriptMap);
285    OwnPtr<UserScriptVector>& scriptsInWorld = m_userScripts->add(world, nullptr).iterator->value;
286    if (!scriptsInWorld)
287        scriptsInWorld = adoptPtr(new UserScriptVector);
288    scriptsInWorld->append(userScript.release());
289}
290
291void PageGroup::addUserStyleSheetToWorld(DOMWrapperWorld* world, const String& source, const KURL& url,
292                                         const Vector<String>& whitelist, const Vector<String>& blacklist,
293                                         UserContentInjectedFrames injectedFrames,
294                                         UserStyleLevel level,
295                                         UserStyleInjectionTime injectionTime)
296{
297    ASSERT_ARG(world, world);
298
299    OwnPtr<UserStyleSheet> userStyleSheet = adoptPtr(new UserStyleSheet(source, url, whitelist, blacklist, injectedFrames, level));
300    if (!m_userStyleSheets)
301        m_userStyleSheets = adoptPtr(new UserStyleSheetMap);
302    OwnPtr<UserStyleSheetVector>& styleSheetsInWorld = m_userStyleSheets->add(world, nullptr).iterator->value;
303    if (!styleSheetsInWorld)
304        styleSheetsInWorld = adoptPtr(new UserStyleSheetVector);
305    styleSheetsInWorld->append(userStyleSheet.release());
306
307    if (injectionTime == InjectInExistingDocuments)
308        invalidateInjectedStyleSheetCacheInAllFrames();
309}
310
311void PageGroup::removeUserScriptFromWorld(DOMWrapperWorld* world, const KURL& url)
312{
313    ASSERT_ARG(world, world);
314
315    if (!m_userScripts)
316        return;
317
318    UserScriptMap::iterator it = m_userScripts->find(world);
319    if (it == m_userScripts->end())
320        return;
321
322    UserScriptVector* scripts = it->value.get();
323    for (int i = scripts->size() - 1; i >= 0; --i) {
324        if (scripts->at(i)->url() == url)
325            scripts->remove(i);
326    }
327
328    if (scripts->isEmpty())
329        m_userScripts->remove(it);
330}
331
332void PageGroup::removeUserStyleSheetFromWorld(DOMWrapperWorld* world, const KURL& url)
333{
334    ASSERT_ARG(world, world);
335
336    if (!m_userStyleSheets)
337        return;
338
339    UserStyleSheetMap::iterator it = m_userStyleSheets->find(world);
340    bool sheetsChanged = false;
341    if (it == m_userStyleSheets->end())
342        return;
343
344    UserStyleSheetVector* stylesheets = it->value.get();
345    for (int i = stylesheets->size() - 1; i >= 0; --i) {
346        if (stylesheets->at(i)->url() == url) {
347            stylesheets->remove(i);
348            sheetsChanged = true;
349        }
350    }
351
352    if (!sheetsChanged)
353        return;
354
355    if (stylesheets->isEmpty())
356        m_userStyleSheets->remove(it);
357
358    invalidateInjectedStyleSheetCacheInAllFrames();
359}
360
361void PageGroup::removeUserScriptsFromWorld(DOMWrapperWorld* world)
362{
363    ASSERT_ARG(world, world);
364
365    if (!m_userScripts)
366        return;
367
368    UserScriptMap::iterator it = m_userScripts->find(world);
369    if (it == m_userScripts->end())
370        return;
371
372    m_userScripts->remove(it);
373}
374
375void 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    m_userStyleSheets->remove(it);
387
388    invalidateInjectedStyleSheetCacheInAllFrames();
389}
390
391void PageGroup::removeAllUserContent()
392{
393    m_userScripts.clear();
394
395    if (m_userStyleSheets) {
396        m_userStyleSheets.clear();
397        invalidateInjectedStyleSheetCacheInAllFrames();
398    }
399}
400
401void PageGroup::invalidateInjectedStyleSheetCacheInAllFrames()
402{
403    // Clear our cached sheets and have them just reparse.
404    HashSet<Page*>::const_iterator end = m_pages.end();
405    for (HashSet<Page*>::const_iterator it = m_pages.begin(); it != end; ++it) {
406        for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
407            frame->document()->styleSheetCollection()->invalidateInjectedStyleSheetCache();
408            frame->document()->styleResolverChanged(DeferRecalcStyle);
409        }
410    }
411}
412
413#if ENABLE(VIDEO_TRACK)
414void PageGroup::captionPreferencesChanged()
415{
416    for (HashSet<Page*>::iterator i = m_pages.begin(); i != m_pages.end(); ++i)
417        (*i)->captionPreferencesChanged();
418    pageCache()->markPagesForCaptionPreferencesChanged();
419}
420
421CaptionUserPreferences* PageGroup::captionPreferences()
422{
423    if (!m_captionPreferences)
424#if (PLATFORM(MAC) && !PLATFORM(IOS)) || HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK)
425        m_captionPreferences = CaptionUserPreferencesMediaAF::create(this);
426#else
427        m_captionPreferences = CaptionUserPreferences::create(this);
428#endif
429
430    return m_captionPreferences.get();
431}
432
433#endif
434
435} // namespace WebCore
436