1/* 2 * Copyright (C) 2010 Google, Inc. All Rights Reserved. 3 * Copyright (C) 2013 Apple, Inc. All Rights Reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "HTMLParserScheduler.h" 29 30#include "Document.h" 31#include "FrameView.h" 32#include "HTMLDocumentParser.h" 33#include "Page.h" 34 35// defaultParserChunkSize is used to define how many tokens the parser will 36// process before checking against parserTimeLimit and possibly yielding. 37// This is a performance optimization to prevent checking after every token. 38static const int defaultParserChunkSize = 4096; 39 40// defaultParserTimeLimit is the seconds the parser will run in one write() call 41// before yielding. Inline <script> execution can cause it to exceed the limit. 42// FIXME: We would like this value to be 0.2. 43static const double defaultParserTimeLimit = 0.500; 44 45namespace WebCore { 46 47static double parserTimeLimit(Page* page) 48{ 49 // We're using the poorly named customHTMLTokenizerTimeDelay setting. 50 if (page && page->hasCustomHTMLTokenizerTimeDelay()) 51 return page->customHTMLTokenizerTimeDelay(); 52 return defaultParserTimeLimit; 53} 54 55ActiveParserSession::ActiveParserSession(Document* document) 56 : m_document(document) 57{ 58 if (!m_document) 59 return; 60 m_document->incrementActiveParserCount(); 61} 62 63ActiveParserSession::~ActiveParserSession() 64{ 65 if (!m_document) 66 return; 67 m_document->decrementActiveParserCount(); 68} 69 70PumpSession::PumpSession(unsigned& nestingLevel, Document* document) 71 : NestingLevelIncrementer(nestingLevel) 72 , ActiveParserSession(document) 73 // Setting processedTokens to INT_MAX causes us to check for yields 74 // after any token during any parse where yielding is allowed. 75 // At that time we'll initialize startTime. 76 , processedTokens(INT_MAX) 77 , startTime(0) 78 , needsYield(false) 79 , didSeeScript(false) 80{ 81} 82 83PumpSession::~PumpSession() 84{ 85} 86 87HTMLParserScheduler::HTMLParserScheduler(HTMLDocumentParser& parser) 88 : m_parser(parser) 89 , m_parserTimeLimit(parserTimeLimit(m_parser.document()->page())) 90 , m_parserChunkSize(defaultParserChunkSize) 91 , m_continueNextChunkTimer(this, &HTMLParserScheduler::continueNextChunkTimerFired) 92 , m_isSuspendedWithActiveTimer(false) 93#if !ASSERT_DISABLED 94 , m_suspended(false) 95#endif 96{ 97} 98 99HTMLParserScheduler::~HTMLParserScheduler() 100{ 101 m_continueNextChunkTimer.stop(); 102} 103 104void HTMLParserScheduler::continueNextChunkTimerFired(Timer<HTMLParserScheduler>& timer) 105{ 106 ASSERT(!m_suspended); 107 ASSERT_UNUSED(timer, &timer == &m_continueNextChunkTimer); 108 109 // FIXME: The timer class should handle timer priorities instead of this code. 110 // If a layout is scheduled, wait again to let the layout timer run first. 111 if (m_parser.document()->isLayoutTimerActive()) { 112 m_continueNextChunkTimer.startOneShot(0); 113 return; 114 } 115 m_parser.resumeParsingAfterYield(); 116} 117 118void HTMLParserScheduler::checkForYieldBeforeScript(PumpSession& session) 119{ 120 // If we've never painted before and a layout is pending, yield prior to running 121 // scripts to give the page a chance to paint earlier. 122 Document* document = m_parser.document(); 123 bool needsFirstPaint = document->view() && !document->view()->hasEverPainted(); 124 if (needsFirstPaint && document->isLayoutTimerActive()) 125 session.needsYield = true; 126 session.didSeeScript = true; 127} 128 129void HTMLParserScheduler::scheduleForResume() 130{ 131 ASSERT(!m_suspended); 132 m_continueNextChunkTimer.startOneShot(0); 133} 134 135void HTMLParserScheduler::suspend() 136{ 137 ASSERT(!m_suspended); 138 ASSERT(!m_isSuspendedWithActiveTimer); 139#if !ASSERT_DISABLED 140 m_suspended = true; 141#endif 142 143 if (!m_continueNextChunkTimer.isActive()) 144 return; 145 m_isSuspendedWithActiveTimer = true; 146 m_continueNextChunkTimer.stop(); 147} 148 149void HTMLParserScheduler::resume() 150{ 151 ASSERT(m_suspended); 152 ASSERT(!m_continueNextChunkTimer.isActive()); 153#if !ASSERT_DISABLED 154 m_suspended = false; 155#endif 156 157 if (!m_isSuspendedWithActiveTimer) 158 return; 159 m_isSuspendedWithActiveTimer = false; 160 m_continueNextChunkTimer.startOneShot(0); 161} 162 163} 164