1/*
2 * Copyright (C) 2008 Apple Inc.
3 * Copyright (C) 2010 Google Inc.
4 * Copyright (C) 2011 Zoltan Horvath (hzoltan@inf.u-szeged.hu) University of Szeged
5 *
6 * All Rights Reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "config.h"
31#include "HTMLPreloadScannerThread.h"
32
33#include "Document.h"
34#include "HTMLDocumentParser.h"
35#include "HTMLLinkElement.h"
36#include "HTMLNames.h"
37#include "HTMLParserIdioms.h"
38#include "HTMLPreloadScanner.h"
39#include "InputType.h"
40#include "MediaList.h"
41#include "MediaQueryEvaluator.h"
42
43namespace WebCore {
44
45using namespace HTMLNames;
46
47static String bodyTagString;
48static String charsetAttrString;
49static String hrefAttrString;
50static String imgTagString;
51static String inputTagString;
52static String linkTagString;
53
54static String mediaAttrString;
55static String relAttrString;
56static String scriptTagString;
57static String srcAttrString;
58static String styleTagString;
59static String typeAttrString;
60
61class Task {
62public:
63 Task(const HTMLToken& token)
64 : m_linkIsStyleSheet(false)
65 , m_linkMediaAttributeIsScreen(true)
66 , m_inputIsImage(false)
67 {
68 m_tagName = String(token.name().data(), token.name().size()).threadsafeCopy();
69 processAttributes(token.attributes());
70 }
71
72 void processAttributes(const HTMLToken::AttributeList& attributes)
73 {
74 if (m_tagName != imgTagString
75 && m_tagName != inputTagString
76 && m_tagName != linkTagString
77 && m_tagName != scriptTagString)
78 return;
79
80 for (HTMLToken::AttributeList::const_iterator iter = attributes.begin();
81 iter != attributes.end(); ++iter) {
82
83 String attributeName(iter->m_name.data(), iter->m_name.size());
84 String attributeValue(iter->m_value.data(), iter->m_value.size());
85
86 if (attributeName == charsetAttrString)
87 m_charset = attributeValue;
88
89 if (m_tagName == scriptTagString || m_tagName == imgTagString) {
90 if (attributeName == srcAttrString)
91 setUrlToLoad(attributeValue);
92 } else if (m_tagName == linkTagString) {
93 if (attributeName == hrefAttrString)
94 setUrlToLoad(attributeValue);
95 else if (attributeName == relAttrString)
96 m_linkIsStyleSheet = relAttributeIsStyleSheet(attributeValue);
97 else if (attributeName == mediaAttrString)
98 m_linkMediaAttributeIsScreen = linkMediaAttributeIsScreen(attributeValue);
99 } else if (m_tagName == inputTagString) {
100 if (attributeName == srcAttrString)
101 setUrlToLoad(attributeValue);
102 else if (attributeName == typeAttrString)
103 m_inputIsImage = equalIgnoringCase(attributeValue, InputTypeNames::image());
104 }
105 }
106 }
107
108 static bool relAttributeIsStyleSheet(const String& attributeValue)
109 {
110 LinkRelAttribute rel(attributeValue);
111 return rel.m_isStyleSheet && !rel.m_isAlternate && rel.m_iconType == InvalidIcon && !rel.m_isDNSPrefetch;
112 }
113
114 static bool linkMediaAttributeIsScreen(const String& attributeValue)
115 {
116 if (attributeValue.isEmpty())
117 return true;
118 RefPtr<MediaList> mediaList = MediaList::createAllowingDescriptionSyntax(attributeValue);
119
120 // Only preload screen media stylesheets. Used this way, the evaluator evaluates to true for any
121 // rules containing complex queries (full evaluation is possible but it requires a frame and a style selector which
122 // may be problematic here).
123 MediaQueryEvaluator mediaQueryEvaluator("screen");
124 return mediaQueryEvaluator.eval(mediaList.get());
125 }
126
127 void setUrlToLoad(const String& attributeValue)
128 {
129 // We only respect the first src/href, per HTML5:
130 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#attribute-name-state
131 if (!m_urlToLoad.isEmpty())
132 return;
133 m_urlToLoad = stripLeadingAndTrailingHTMLSpaces(attributeValue);
134 }
135
136 const String& tagName() const { return m_tagName; }
137 const String& urlToLoad() const { return m_urlToLoad; }
138 const String& charset() const { return m_charset; }
139 bool linkIsStyleSheet() const { return m_linkIsStyleSheet; }
140 bool linkMediaAttributeIsScreen() const { return m_linkMediaAttributeIsScreen; }
141 bool inputIsImage() const { return m_inputIsImage; }
142
143private:
144 String m_tagName;
145 String m_urlToLoad;
146 String m_charset;
147 bool m_linkIsStyleSheet;
148 bool m_linkMediaAttributeIsScreen;
149 bool m_inputIsImage;
150};
151
152void* htmlPreloadScannerThreadStart(void* arg)
153{
154 HTMLPreloadScannerThread* thread = new HTMLPreloadScannerThread;
155 HTMLPreloadScanner* preloader = static_cast<HTMLPreloadScanner*>(arg);
156
157 preloader->s_preloadScannerThread = thread;
158 thread->m_urlsToLoadQueue = preloader->m_urlsToLoadQueue;
159
160 bodyTagString = bodyTag.toString();
161 imgTagString = imgTag.toString();
162 inputTagString = inputTag.toString();
163 linkTagString = linkTag.toString();
164 scriptTagString = scriptTag.toString();
165
166 charsetAttrString = charsetAttr.toString();
167 hrefAttrString = hrefAttr.toString();
168 mediaAttrString = mediaAttr.toString();
169 relAttrString = relAttr.toString();
170 srcAttrString = srcAttr.toString();
171 styleTagString = styleTag.toString();
172 typeAttrString = typeAttr.toString();
173
174 while (PassOwnPtr<DocumentToScan> docToScan = thread->m_documentsToScan->waitForMessage())
175 thread->scan(docToScan);
176
177 return arg;
178}
179
180HTMLPreloadScannerThread::HTMLPreloadScannerThread()
181 : m_documentsToScan(new MessageQueue<DocumentToScan>())
182{
183}
184
185void HTMLPreloadScannerThread::scan(PassOwnPtr<DocumentToScan> doc)
186{
187 OwnPtr<DocumentToScan> docToScan(doc);
188
189 if (docToScan->source.isEmpty())
190 return;
191
192 while (docToScan->tokenizer->nextToken(docToScan->source, docToScan->token)) {
193 processToken(docToScan.get());
194 docToScan->token.clear();
195 }
196
197 docToScan.clear();
198}
199
200void HTMLPreloadScannerThread::processToken(DocumentToScan* docToScan)
201{
202 if (docToScan->token.type() == HTMLToken::Character)
203 docToScan->cssScanner.scan(docToScan->token);
204 else if (docToScan->token.type() == HTMLToken::EndTag)
205 docToScan->cssScanner.reset();
206
207 if (docToScan->token.type() != HTMLToken::StartTag)
208 return;
209
210 Task task(docToScan->token);
211
212 if (task.urlToLoad().isEmpty())
213 return;
214
215 m_urlsToLoadQueue->append(adoptPtr(new Preload(docToScan->document, task.linkIsStyleSheet(),
216 task.linkMediaAttributeIsScreen(),
217 task.inputIsImage(), task.urlToLoad().threadsafeCopy(),
218 task.tagName().threadsafeCopy(), task.charset().threadsafeCopy())));
219}
220
221void HTMLPreloadScannerThread::addTask(Document* document, String source)
222{
223 m_documentsToScan->append(adoptPtr(new DocumentToScan(document, source)));
224}
225
226}