OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2009 Google 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 are | |
6 * met: | |
7 * | |
8 * * Redistributions of source code must retain the above copyright | |
9 * notice, this list of conditions and the following disclaimer. | |
10 * * Redistributions in binary form must reproduce the above | |
11 * copyright notice, this list of conditions and the following disclaimer | |
12 * in the documentation and/or other materials provided with the | |
13 * distribution. | |
14 * * Neither the name of Google Inc. nor the names of its | |
15 * contributors may be used to endorse or promote products derived from | |
16 * this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 */ | |
30 | |
31 // How ownership works | |
32 // ------------------- | |
33 // | |
34 // Big oh represents a refcounted relationship: owner O--- ownee | |
35 // | |
36 // WebView (for the toplevel frame only) | |
37 // O | |
38 // | | |
39 // Page O------- Frame (m_mainFrame) O-------O FrameView | |
40 // || | |
41 // || | |
42 // FrameLoader O-------- WebFrame (via FrameLoaderClient) | |
43 // | |
44 // FrameLoader and Frame are formerly one object that was split apart because | |
45 // it got too big. They basically have the same lifetime, hence the double line. | |
46 // | |
47 // WebFrame is refcounted and has one ref on behalf of the FrameLoader/Frame. | |
48 // This is not a normal reference counted pointer because that would require | |
49 // changing WebKit code that we don't control. Instead, it is created with this | |
50 // ref initially and it is removed when the FrameLoader is getting destroyed. | |
51 // | |
52 // WebFrames are created in two places, first in WebViewImpl when the root | |
53 // frame is created, and second in WebFrame::CreateChildFrame when sub-frames | |
54 // are created. WebKit will hook up this object to the FrameLoader/Frame | |
55 // and the refcount will be correct. | |
56 // | |
57 // How frames are destroyed | |
58 // ------------------------ | |
59 // | |
60 // The main frame is never destroyed and is re-used. The FrameLoader is re-used | |
61 // and a reference to the main frame is kept by the Page. | |
62 // | |
63 // When frame content is replaced, all subframes are destroyed. This happens | |
64 // in FrameLoader::detachFromParent for each subframe. | |
65 // | |
66 // Frame going away causes the FrameLoader to get deleted. In FrameLoader's | |
67 // destructor, it notifies its client with frameLoaderDestroyed. This calls | |
68 // WebFrame::Closing and then derefs the WebFrame and will cause it to be | |
69 // deleted (unless an external someone is also holding a reference). | |
70 | |
71 #include "config.h" | |
72 #include "WebFrameImpl.h" | |
73 | |
74 #include "Chrome.h" | |
75 #include "ChromiumBridge.h" | |
76 #include "ClipboardUtilitiesChromium.h" | |
77 #include "Console.h" | |
78 #include "Document.h" | |
79 #include "DocumentFragment.h" // Only needed for ReplaceSelectionCommand.h :( | |
80 #include "DocumentLoader.h" | |
81 #include "DocumentMarker.h" | |
82 #include "DOMUtilitiesPrivate.h" | |
83 #include "DOMWindow.h" | |
84 #include "Editor.h" | |
85 #include "EventHandler.h" | |
86 #include "FormState.h" | |
87 #include "FrameChromium.h" | |
88 #include "FrameLoader.h" | |
89 #include "FrameLoadRequest.h" | |
90 #include "FrameTree.h" | |
91 #include "FrameView.h" | |
92 #include "GraphicsContext.h" | |
93 #include "HistoryItem.h" | |
94 #include "HTMLCollection.h" | |
95 #include "HTMLFormElement.h" | |
96 #include "HTMLFrameOwnerElement.h" | |
97 #include "HTMLHeadElement.h" | |
98 #include "HTMLInputElement.h" | |
99 #include "HTMLLinkElement.h" | |
100 #include "HTMLNames.h" | |
101 #include "InspectorController.h" | |
102 #include "markup.h" | |
103 #include "Page.h" | |
104 #include "PlatformContextSkia.h" | |
105 #include "PrintContext.h" | |
106 #include "RenderFrame.h" | |
107 #include "RenderView.h" | |
108 #include "RenderWidget.h" | |
109 #include "ReplaceSelectionCommand.h" | |
110 #include "ResourceHandle.h" | |
111 #include "ResourceRequest.h" | |
112 #include "ScriptController.h" | |
113 #include "ScriptSourceCode.h" | |
114 #include "ScriptValue.h" | |
115 #include "ScrollbarTheme.h" | |
116 #include "ScrollTypes.h" | |
117 #include "SelectionController.h" | |
118 #include "Settings.h" | |
119 #include "SkiaUtils.h" | |
120 #include "SubstituteData.h" | |
121 #include "TextAffinity.h" | |
122 #include "TextIterator.h" | |
123 #include "WebConsoleMessage.h" | |
124 #include "WebDataSourceImpl.h" | |
125 #include "WebFindOptions.h" | |
126 #include "WebFormElement.h" | |
127 #include "WebFrameClient.h" | |
128 #include "WebHistoryItem.h" | |
129 #include "WebInputElement.h" | |
130 #include "WebPasswordAutocompleteListener.h" | |
131 #include "WebRange.h" | |
132 #include "WebRect.h" | |
133 #include "WebScriptSource.h" | |
134 #include "WebSecurityOrigin.h" | |
135 #include "WebSize.h" | |
136 #include "WebURLError.h" | |
137 #include "WebVector.h" | |
138 #include "WebViewImpl.h" | |
139 #include "XPathResult.h" | |
140 | |
141 #include <algorithm> | |
142 #include <wtf/CurrentTime.h> | |
143 | |
144 | |
145 #if PLATFORM(DARWIN) | |
146 #include "LocalCurrentGraphicsContext.h" | |
147 #endif | |
148 | |
149 #if PLATFORM(LINUX) | |
150 #include <gdk/gdk.h> | |
151 #endif | |
152 | |
153 using namespace WebCore; | |
154 | |
155 namespace WebKit { | |
156 | |
157 // Key for a StatsCounter tracking how many WebFrames are active. | |
158 static const char* const webFrameActiveCount = "WebFrameActiveCount"; | |
159 | |
160 static const char* const osdType = "application/opensearchdescription+xml"; | |
161 static const char* const osdRel = "search"; | |
162 | |
163 // Backend for contentAsPlainText, this is a recursive function that gets | |
164 // the text for the current frame and all of its subframes. It will append | |
165 // the text of each frame in turn to the |output| up to |maxChars| length. | |
166 // | |
167 // The |frame| must be non-null. | |
168 static void frameContentAsPlainText(size_t maxChars, Frame* frame, | |
169 Vector<UChar>* output) | |
170 { | |
171 Document* doc = frame->document(); | |
172 if (!doc) | |
173 return; | |
174 | |
175 if (!frame->view()) | |
176 return; | |
177 | |
178 // TextIterator iterates over the visual representation of the DOM. As such, | |
179 // it requires you to do a layout before using it (otherwise it'll crash). | |
180 if (frame->view()->needsLayout()) | |
181 frame->view()->layout(); | |
182 | |
183 // Select the document body. | |
184 RefPtr<Range> range(doc->createRange()); | |
185 ExceptionCode exception = 0; | |
186 range->selectNodeContents(doc->body(), exception); | |
187 | |
188 if (!exception) { | |
189 // The text iterator will walk nodes giving us text. This is similar to | |
190 // the plainText() function in TextIterator.h, but we implement the maximum | |
191 // size and also copy the results directly into a wstring, avoiding the | |
192 // string conversion. | |
193 for (TextIterator it(range.get()); !it.atEnd(); it.advance()) { | |
194 const UChar* chars = it.characters(); | |
195 if (!chars) { | |
196 if (it.length()) { | |
197 // It appears from crash reports that an iterator can get into a state | |
198 // where the character count is nonempty but the character pointer is | |
199 // null. advance()ing it will then just add that many to the null | |
200 // pointer which won't be caught in a null check but will crash. | |
201 // | |
202 // A null pointer and 0 length is common for some nodes. | |
203 // | |
204 // IF YOU CATCH THIS IN A DEBUGGER please let brettw know. We don't | |
205 // currently understand the conditions for this to occur. Ideally, the | |
206 // iterators would never get into the condition so we should fix them | |
207 // if we can. | |
208 ASSERT_NOT_REACHED(); | |
209 break; | |
210 } | |
211 | |
212 // Just got a null node, we can forge ahead! | |
213 continue; | |
214 } | |
215 size_t toAppend = | |
216 std::min(static_cast<size_t>(it.length()), maxChars - output->size()); | |
217 output->append(chars, toAppend); | |
218 if (output->size() >= maxChars) | |
219 return; // Filled up the buffer. | |
220 } | |
221 } | |
222 | |
223 // The separator between frames when the frames are converted to plain text. | |
224 const UChar frameSeparator[] = { '\n', '\n' }; | |
225 const size_t frameSeparatorLen = 2; | |
226 | |
227 // Recursively walk the children. | |
228 FrameTree* frameTree = frame->tree(); | |
229 for (Frame* curChild = frameTree->firstChild(); curChild; curChild = curChild->tree()->nextSibling()) { | |
230 // Make sure the frame separator won't fill up the buffer, and give up if | |
231 // it will. The danger is if the separator will make the buffer longer than | |
232 // maxChars. This will cause the computation above: | |
233 // maxChars - output->size() | |
234 // to be a negative number which will crash when the subframe is added. | |
235 if (output->size() >= maxChars - frameSeparatorLen) | |
236 return; | |
237 | |
238 output->append(frameSeparator, frameSeparatorLen); | |
239 frameContentAsPlainText(maxChars, curChild, output); | |
240 if (output->size() >= maxChars) | |
241 return; // Filled up the buffer. | |
242 } | |
243 } | |
244 | |
245 // Simple class to override some of PrintContext behavior. | |
246 class ChromePrintContext : public PrintContext, public Noncopyable { | |
247 public: | |
248 ChromePrintContext(Frame* frame) | |
249 : PrintContext(frame) | |
250 , m_printedPageWidth(0) | |
251 { | |
252 } | |
253 | |
254 void begin(float width) | |
255 { | |
256 ASSERT(!m_printedPageWidth); | |
257 m_printedPageWidth = width; | |
258 PrintContext::begin(m_printedPageWidth); | |
259 } | |
260 | |
261 float getPageShrink(int pageNumber) const | |
262 { | |
263 IntRect pageRect = m_pageRects[pageNumber]; | |
264 return m_printedPageWidth / pageRect.width(); | |
265 } | |
266 | |
267 // Spools the printed page, a subrect of m_frame. Skip the scale step. | |
268 // NativeTheme doesn't play well with scaling. Scaling is done browser side | |
269 // instead. Returns the scale to be applied. | |
270 float spoolPage(GraphicsContext& ctx, int pageNumber) | |
271 { | |
272 IntRect pageRect = m_pageRects[pageNumber]; | |
273 float scale = m_printedPageWidth / pageRect.width(); | |
274 | |
275 ctx.save(); | |
276 ctx.translate(static_cast<float>(-pageRect.x()), | |
277 static_cast<float>(-pageRect.y())); | |
278 ctx.clip(pageRect); | |
279 m_frame->view()->paintContents(&ctx, pageRect); | |
280 ctx.restore(); | |
281 return scale; | |
282 } | |
283 | |
284 private: | |
285 // Set when printing. | |
286 float m_printedPageWidth; | |
287 }; | |
288 | |
289 static WebDataSource* DataSourceForDocLoader(DocumentLoader* loader) | |
290 { | |
291 return loader ? WebDataSourceImpl::fromDocumentLoader(loader) : 0; | |
292 } | |
293 | |
294 | |
295 // WebFrame ------------------------------------------------------------------- | |
296 | |
297 class WebFrameImpl::DeferredScopeStringMatches { | |
298 public: | |
299 DeferredScopeStringMatches(WebFrameImpl* webFrame, | |
300 int identifier, | |
301 const WebString& searchText, | |
302 const WebFindOptions& options, | |
303 bool reset) | |
304 : m_timer(this, &DeferredScopeStringMatches::doTimeout) | |
305 , m_webFrame(webFrame) | |
306 , m_identifier(identifier) | |
307 , m_searchText(searchText) | |
308 , m_options(options) | |
309 , m_reset(reset) | |
310 { | |
311 m_timer.startOneShot(0.0); | |
312 } | |
313 | |
314 private: | |
315 void doTimeout(Timer<DeferredScopeStringMatches>*) | |
316 { | |
317 m_webFrame->callScopeStringMatches( | |
318 this, m_identifier, m_searchText, m_options, m_reset); | |
319 } | |
320 | |
321 Timer<DeferredScopeStringMatches> m_timer; | |
322 RefPtr<WebFrameImpl> m_webFrame; | |
323 int m_identifier; | |
324 WebString m_searchText; | |
325 WebFindOptions m_options; | |
326 bool m_reset; | |
327 }; | |
328 | |
329 | |
330 // WebFrame ------------------------------------------------------------------- | |
331 | |
332 WebFrame* WebFrame::frameForEnteredContext() | |
333 { | |
334 Frame* frame = | |
335 ScriptController::retrieveFrameForEnteredContext(); | |
336 return WebFrameImpl::fromFrame(frame); | |
337 } | |
338 | |
339 WebFrame* WebFrame::frameForCurrentContext() | |
340 { | |
341 Frame* frame = | |
342 ScriptController::retrieveFrameForCurrentContext(); | |
343 return WebFrameImpl::fromFrame(frame); | |
344 } | |
345 | |
346 WebString WebFrameImpl::name() const | |
347 { | |
348 return m_frame->tree()->name(); | |
349 } | |
350 | |
351 WebURL WebFrameImpl::url() const | |
352 { | |
353 const WebDataSource* ds = dataSource(); | |
354 if (!ds) | |
355 return WebURL(); | |
356 return ds->request().url(); | |
357 } | |
358 | |
359 WebURL WebFrameImpl::favIconURL() const | |
360 { | |
361 FrameLoader* frameLoader = m_frame->loader(); | |
362 // The URL to the favicon may be in the header. As such, only | |
363 // ask the loader for the favicon if it's finished loading. | |
364 if (frameLoader->state() == FrameStateComplete) { | |
365 const KURL& url = frameLoader->iconURL(); | |
366 if (!url.isEmpty()) | |
367 return url; | |
368 } | |
369 return WebURL(); | |
370 } | |
371 | |
372 WebURL WebFrameImpl::openSearchDescriptionURL() const | |
373 { | |
374 FrameLoader* frameLoader = m_frame->loader(); | |
375 if (frameLoader->state() == FrameStateComplete | |
376 && m_frame->document() && m_frame->document()->head() | |
377 && !m_frame->tree()->parent()) { | |
378 HTMLHeadElement* head = m_frame->document()->head(); | |
379 if (head) { | |
380 RefPtr<HTMLCollection> children = head->children(); | |
381 for (Node* child = children->firstItem(); child; child = children->nextItem()) { | |
382 HTMLLinkElement* linkElement = toHTMLLinkElement(child); | |
383 if (linkElement | |
384 && linkElement->type() == osdType | |
385 && linkElement->rel() == osdRel | |
386 && !linkElement->href().isEmpty()) | |
387 return linkElement->href(); | |
388 } | |
389 } | |
390 } | |
391 return WebURL(); | |
392 } | |
393 | |
394 WebSize WebFrameImpl::scrollOffset() const | |
395 { | |
396 FrameView* view = frameView(); | |
397 if (view) | |
398 return view->scrollOffset(); | |
399 | |
400 return WebSize(); | |
401 } | |
402 | |
403 WebSize WebFrameImpl::contentsSize() const | |
404 { | |
405 return frame()->view()->contentsSize(); | |
406 } | |
407 | |
408 int WebFrameImpl::contentsPreferredWidth() const | |
409 { | |
410 if (m_frame->document() && m_frame->document()->renderView()) | |
411 return m_frame->document()->renderView()->minPrefWidth(); | |
412 return 0; | |
413 } | |
414 | |
415 bool WebFrameImpl::hasVisibleContent() const | |
416 { | |
417 return frame()->view()->visibleWidth() > 0 && frame()->view()->visibleHeight() > 0; | |
418 } | |
419 | |
420 WebView* WebFrameImpl::view() const | |
421 { | |
422 return viewImpl(); | |
423 } | |
424 | |
425 WebFrame* WebFrameImpl::opener() const | |
426 { | |
427 Frame* opener = 0; | |
428 if (m_frame) | |
429 opener = m_frame->loader()->opener(); | |
430 return fromFrame(opener); | |
431 } | |
432 | |
433 WebFrame* WebFrameImpl::parent() const | |
434 { | |
435 Frame* parent = 0; | |
436 if (m_frame) | |
437 parent = m_frame->tree()->parent(); | |
438 return fromFrame(parent); | |
439 } | |
440 | |
441 WebFrame* WebFrameImpl::top() const | |
442 { | |
443 if (m_frame) | |
444 return fromFrame(m_frame->tree()->top()); | |
445 | |
446 return 0; | |
447 } | |
448 | |
449 WebFrame* WebFrameImpl::firstChild() const | |
450 { | |
451 return fromFrame(frame()->tree()->firstChild()); | |
452 } | |
453 | |
454 WebFrame* WebFrameImpl::lastChild() const | |
455 { | |
456 return fromFrame(frame()->tree()->lastChild()); | |
457 } | |
458 | |
459 WebFrame* WebFrameImpl::nextSibling() const | |
460 { | |
461 return fromFrame(frame()->tree()->nextSibling()); | |
462 } | |
463 | |
464 WebFrame* WebFrameImpl::previousSibling() const | |
465 { | |
466 return fromFrame(frame()->tree()->previousSibling()); | |
467 } | |
468 | |
469 WebFrame* WebFrameImpl::traverseNext(bool wrap) const | |
470 { | |
471 return fromFrame(frame()->tree()->traverseNextWithWrap(wrap)); | |
472 } | |
473 | |
474 WebFrame* WebFrameImpl::traversePrevious(bool wrap) const | |
475 { | |
476 return fromFrame(frame()->tree()->traversePreviousWithWrap(wrap)); | |
477 } | |
478 | |
479 WebFrame* WebFrameImpl::findChildByName(const WebString& name) const | |
480 { | |
481 return fromFrame(frame()->tree()->child(name)); | |
482 } | |
483 | |
484 WebFrame* WebFrameImpl::findChildByExpression(const WebString& xpath) const | |
485 { | |
486 if (xpath.isEmpty()) | |
487 return 0; | |
488 | |
489 Document* document = m_frame->document(); | |
490 | |
491 ExceptionCode ec = 0; | |
492 PassRefPtr<XPathResult> xpathResult = | |
493 document->evaluate(xpath, | |
494 document, | |
495 0, // namespace | |
496 XPathResult::ORDERED_NODE_ITERATOR_TYPE, | |
497 0, // XPathResult object | |
498 ec); | |
499 if (!xpathResult.get()) | |
500 return 0; | |
501 | |
502 Node* node = xpathResult->iterateNext(ec); | |
503 | |
504 if (!node || !node->isFrameOwnerElement()) | |
505 return 0; | |
506 HTMLFrameOwnerElement* frameElement = | |
507 static_cast<HTMLFrameOwnerElement*>(node); | |
508 return fromFrame(frameElement->contentFrame()); | |
509 } | |
510 | |
511 void WebFrameImpl::forms(WebVector<WebFormElement>& results) const | |
512 { | |
513 if (!m_frame) | |
514 return; | |
515 | |
516 RefPtr<HTMLCollection> forms = m_frame->document()->forms(); | |
517 size_t formCount = forms->length(); | |
518 | |
519 WebVector<WebFormElement> temp(formCount); | |
520 for (size_t i = 0; i < formCount; ++i) { | |
521 Node* node = forms->item(i); | |
522 // Strange but true, sometimes item can be 0. | |
523 if (node) | |
524 temp[i] = static_cast<HTMLFormElement*>(node); | |
525 } | |
526 results.swap(temp); | |
527 } | |
528 | |
529 WebSecurityOrigin WebFrameImpl::securityOrigin() const | |
530 { | |
531 if (!m_frame || !m_frame->document()) | |
532 return WebSecurityOrigin(); | |
533 | |
534 return WebSecurityOrigin(m_frame->document()->securityOrigin()); | |
535 } | |
536 | |
537 void WebFrameImpl::grantUniversalAccess() | |
538 { | |
539 ASSERT(m_frame && m_frame->document()); | |
540 if (m_frame && m_frame->document()) | |
541 m_frame->document()->securityOrigin()->grantUniversalAccess(); | |
542 } | |
543 | |
544 NPObject* WebFrameImpl::windowObject() const | |
545 { | |
546 if (!m_frame) | |
547 return 0; | |
548 | |
549 return m_frame->script()->windowScriptNPObject(); | |
550 } | |
551 | |
552 void WebFrameImpl::bindToWindowObject(const WebString& name, NPObject* object) | |
553 { | |
554 ASSERT(m_frame); | |
555 if (!m_frame || !m_frame->script()->isEnabled()) | |
556 return; | |
557 | |
558 String key = name; | |
559 #if USE(V8) | |
560 m_frame->script()->bindToWindowObject(m_frame, key, object); | |
561 #else | |
562 notImplemented(); | |
563 #endif | |
564 } | |
565 | |
566 void WebFrameImpl::executeScript(const WebScriptSource& source) | |
567 { | |
568 m_frame->script()->executeScript( | |
569 ScriptSourceCode(source.code, source.url, source.startLine)); | |
570 } | |
571 | |
572 void WebFrameImpl::executeScriptInNewContext( | |
573 const WebScriptSource* sourcesIn, unsigned numSources, int extensionGroup) | |
574 { | |
575 Vector<ScriptSourceCode> sources; | |
576 | |
577 for (unsigned i = 0; i < numSources; ++i) { | |
578 sources.append(ScriptSourceCode( | |
579 sourcesIn[i].code, sourcesIn[i].url, sourcesIn[i].startLine)); | |
580 } | |
581 | |
582 m_frame->script()->evaluateInNewContext(sources, extensionGroup); | |
583 } | |
584 | |
585 void WebFrameImpl::executeScriptInIsolatedWorld( | |
586 int worldId, const WebScriptSource* sourcesIn, unsigned numSources, | |
587 int extensionGroup) | |
588 { | |
589 Vector<ScriptSourceCode> sources; | |
590 | |
591 for (unsigned i = 0; i < numSources; ++i) { | |
592 sources.append(ScriptSourceCode( | |
593 sourcesIn[i].code, sourcesIn[i].url, sourcesIn[i].startLine)); | |
594 } | |
595 | |
596 m_frame->script()->evaluateInIsolatedWorld(worldId, sources, extensionGroup); | |
597 } | |
598 | |
599 void WebFrameImpl::addMessageToConsole(const WebConsoleMessage& message) | |
600 { | |
601 ASSERT(frame()); | |
602 | |
603 MessageLevel webCoreMessageLevel; | |
604 switch (message.level) { | |
605 case WebConsoleMessage::LevelTip: | |
606 webCoreMessageLevel = TipMessageLevel; | |
607 break; | |
608 case WebConsoleMessage::LevelLog: | |
609 webCoreMessageLevel = LogMessageLevel; | |
610 break; | |
611 case WebConsoleMessage::LevelWarning: | |
612 webCoreMessageLevel = WarningMessageLevel; | |
613 break; | |
614 case WebConsoleMessage::LevelError: | |
615 webCoreMessageLevel = ErrorMessageLevel; | |
616 break; | |
617 default: | |
618 ASSERT_NOT_REACHED(); | |
619 return; | |
620 } | |
621 | |
622 frame()->domWindow()->console()->addMessage( | |
623 OtherMessageSource, LogMessageType, webCoreMessageLevel, message.text, | |
624 1, String()); | |
625 } | |
626 | |
627 void WebFrameImpl::collectGarbage() | |
628 { | |
629 if (!m_frame) | |
630 return; | |
631 if (!m_frame->settings()->isJavaScriptEnabled()) | |
632 return; | |
633 // FIXME: Move this to the ScriptController and make it JS neutral. | |
634 #if USE(V8) | |
635 m_frame->script()->collectGarbage(); | |
636 #else | |
637 notImplemented(); | |
638 #endif | |
639 } | |
640 | |
641 #if USE(V8) | |
642 // Returns the V8 context for this frame, or an empty handle if there is none. | |
643 v8::Local<v8::Context> WebFrameImpl::mainWorldScriptContext() const | |
644 { | |
645 if (!m_frame) | |
646 return v8::Local<v8::Context>(); | |
647 | |
648 return V8Proxy::mainWorldContext(m_frame); | |
649 } | |
650 #endif | |
651 | |
652 bool WebFrameImpl::insertStyleText( | |
653 const WebString& css, const WebString& id) { | |
654 Document* document = frame()->document(); | |
655 if (!document) | |
656 return false; | |
657 Element* documentElement = document->documentElement(); | |
658 if (!documentElement) | |
659 return false; | |
660 | |
661 ExceptionCode err = 0; | |
662 | |
663 if (!id.isEmpty()) { | |
664 Element* oldElement = document->getElementById(id); | |
665 if (oldElement) { | |
666 Node* parent = oldElement->parent(); | |
667 if (!parent) | |
668 return false; | |
669 parent->removeChild(oldElement, err); | |
670 } | |
671 } | |
672 | |
673 RefPtr<Element> stylesheet = document->createElement( | |
674 HTMLNames::styleTag, false); | |
675 if (!id.isEmpty()) | |
676 stylesheet->setAttribute(HTMLNames::idAttr, id); | |
677 stylesheet->setTextContent(css, err); | |
678 ASSERT(!err); | |
679 Node* first = documentElement->firstChild(); | |
680 bool success = documentElement->insertBefore(stylesheet, first, err); | |
681 ASSERT(success); | |
682 return success; | |
683 } | |
684 | |
685 void WebFrameImpl::reload() | |
686 { | |
687 m_frame->loader()->history()->saveDocumentAndScrollState(); | |
688 | |
689 stopLoading(); // Make sure existing activity stops. | |
690 m_frame->loader()->reload(); | |
691 } | |
692 | |
693 void WebFrameImpl::loadRequest(const WebURLRequest& request) | |
694 { | |
695 ASSERT(!request.isNull()); | |
696 const ResourceRequest& resourceRequest = request.toResourceRequest(); | |
697 | |
698 if (resourceRequest.url().protocolIs("javascript")) { | |
699 loadJavaScriptURL(resourceRequest.url()); | |
700 return; | |
701 } | |
702 | |
703 stopLoading(); // Make sure existing activity stops. | |
704 m_frame->loader()->load(resourceRequest, false); | |
705 } | |
706 | |
707 void WebFrameImpl::loadHistoryItem(const WebHistoryItem& item) | |
708 { | |
709 RefPtr<HistoryItem> historyItem = PassRefPtr<HistoryItem>(item); | |
710 ASSERT(historyItem.get()); | |
711 | |
712 stopLoading(); // Make sure existing activity stops. | |
713 | |
714 // If there is no currentItem, which happens when we are navigating in | |
715 // session history after a crash, we need to manufacture one otherwise WebKit | |
716 // hoarks. This is probably the wrong thing to do, but it seems to work. | |
717 RefPtr<HistoryItem> currentItem = m_frame->loader()->history()->currentItem(); | |
718 if (!currentItem) { | |
719 currentItem = HistoryItem::create(); | |
720 currentItem->setLastVisitWasFailure(true); | |
721 m_frame->loader()->history()->setCurrentItem(currentItem.get()); | |
722 viewImpl()->setCurrentHistoryItem(currentItem.get()); | |
723 } | |
724 | |
725 m_frame->loader()->history()->goToItem( | |
726 historyItem.get(), FrameLoadTypeIndexedBackForward); | |
727 } | |
728 | |
729 void WebFrameImpl::loadData(const WebData& data, | |
730 const WebString& mimeType, | |
731 const WebString& textEncoding, | |
732 const WebURL& baseURL, | |
733 const WebURL& unreachableURL, | |
734 bool replace) | |
735 { | |
736 SubstituteData substData(data, mimeType, textEncoding, unreachableURL); | |
737 ASSERT(substData.isValid()); | |
738 | |
739 stopLoading(); // Make sure existing activity stops. | |
740 m_frame->loader()->load(ResourceRequest(baseURL), substData, false); | |
741 if (replace) { | |
742 // Do this to force WebKit to treat the load as replacing the currently | |
743 // loaded page. | |
744 m_frame->loader()->setReplacing(); | |
745 } | |
746 } | |
747 | |
748 void WebFrameImpl::loadHTMLString(const WebData& data, | |
749 const WebURL& baseURL, | |
750 const WebURL& unreachableURL, | |
751 bool replace) | |
752 { | |
753 loadData(data, | |
754 WebString::fromUTF8("text/html"), | |
755 WebString::fromUTF8("UTF-8"), | |
756 baseURL, | |
757 unreachableURL, | |
758 replace); | |
759 } | |
760 | |
761 bool WebFrameImpl::isLoading() const | |
762 { | |
763 if (!m_frame) | |
764 return false; | |
765 return m_frame->loader()->isLoading(); | |
766 } | |
767 | |
768 void WebFrameImpl::stopLoading() | |
769 { | |
770 if (!m_frame) | |
771 return; | |
772 | |
773 // FIXME: Figure out what we should really do here. It seems like a bug | |
774 // that FrameLoader::stopLoading doesn't call stopAllLoaders. | |
775 m_frame->loader()->stopAllLoaders(); | |
776 m_frame->loader()->stopLoading(UnloadEventPolicyNone); | |
777 } | |
778 | |
779 WebDataSource* WebFrameImpl::provisionalDataSource() const | |
780 { | |
781 FrameLoader* frameLoader = m_frame->loader(); | |
782 | |
783 // We regard the policy document loader as still provisional. | |
784 DocumentLoader* docLoader = frameLoader->provisionalDocumentLoader(); | |
785 if (!docLoader) | |
786 docLoader = frameLoader->policyDocumentLoader(); | |
787 | |
788 return DataSourceForDocLoader(docLoader); | |
789 } | |
790 | |
791 WebDataSource* WebFrameImpl::dataSource() const | |
792 { | |
793 return DataSourceForDocLoader(m_frame->loader()->documentLoader()); | |
794 } | |
795 | |
796 WebHistoryItem WebFrameImpl::previousHistoryItem() const | |
797 { | |
798 // We use the previous item here because documentState (filled-out forms) | |
799 // only get saved to history when it becomes the previous item. The caller | |
800 // is expected to query the history item after a navigation occurs, after | |
801 // the desired history item has become the previous entry. | |
802 return WebHistoryItem(viewImpl()->previousHistoryItem()); | |
803 } | |
804 | |
805 WebHistoryItem WebFrameImpl::currentHistoryItem() const | |
806 { | |
807 m_frame->loader()->history()->saveDocumentAndScrollState(); | |
808 | |
809 return WebHistoryItem(m_frame->page()->backForwardList()->currentItem()); | |
810 } | |
811 | |
812 void WebFrameImpl::enableViewSourceMode(bool enable) | |
813 { | |
814 if (m_frame) | |
815 m_frame->setInViewSourceMode(enable); | |
816 } | |
817 | |
818 bool WebFrameImpl::isViewSourceModeEnabled() const | |
819 { | |
820 if (m_frame) | |
821 return m_frame->inViewSourceMode(); | |
822 | |
823 return false; | |
824 } | |
825 | |
826 void WebFrameImpl::setReferrerForRequest( | |
827 WebURLRequest& request, const WebURL& referrerURL) { | |
828 String referrer; | |
829 if (referrerURL.isEmpty()) | |
830 referrer = m_frame->loader()->outgoingReferrer(); | |
831 else | |
832 referrer = referrerURL.spec().utf16(); | |
833 if (SecurityOrigin::shouldHideReferrer(request.url(), referrer)) | |
834 return; | |
835 request.setHTTPHeaderField(WebString::fromUTF8("Referer"), referrer); | |
836 } | |
837 | |
838 void WebFrameImpl::dispatchWillSendRequest(WebURLRequest& request) | |
839 { | |
840 ResourceResponse response; | |
841 m_frame->loader()->client()->dispatchWillSendRequest( | |
842 0, 0, request.toMutableResourceRequest(), response); | |
843 } | |
844 | |
845 void WebFrameImpl::commitDocumentData(const char* data, size_t dataLen) | |
846 { | |
847 DocumentLoader* documentLoader = m_frame->loader()->documentLoader(); | |
848 | |
849 // Set the text encoding. This calls begin() for us. It is safe to call | |
850 // this multiple times (Mac does: page/mac/WebCoreFrameBridge.mm). | |
851 bool userChosen = true; | |
852 String encoding = documentLoader->overrideEncoding(); | |
853 if (encoding.isNull()) { | |
854 userChosen = false; | |
855 encoding = documentLoader->response().textEncodingName(); | |
856 } | |
857 m_frame->loader()->setEncoding(encoding, userChosen); | |
858 | |
859 // NOTE: mac only does this if there is a document | |
860 m_frame->loader()->addData(data, dataLen); | |
861 } | |
862 | |
863 unsigned WebFrameImpl::unloadListenerCount() const | |
864 { | |
865 return frame()->domWindow()->pendingUnloadEventListeners(); | |
866 } | |
867 | |
868 bool WebFrameImpl::isProcessingUserGesture() const | |
869 { | |
870 return frame()->loader()->isProcessingUserGesture(); | |
871 } | |
872 | |
873 bool WebFrameImpl::willSuppressOpenerInNewFrame() const | |
874 { | |
875 return frame()->loader()->suppressOpenerInNewFrame(); | |
876 } | |
877 | |
878 void WebFrameImpl::replaceSelection(const WebString& text) | |
879 { | |
880 RefPtr<DocumentFragment> fragment = createFragmentFromText( | |
881 frame()->selection()->toNormalizedRange().get(), text); | |
882 applyCommand(ReplaceSelectionCommand::create( | |
883 frame()->document(), fragment.get(), false, true, true)); | |
884 } | |
885 | |
886 void WebFrameImpl::insertText(const WebString& text) | |
887 { | |
888 frame()->editor()->insertText(text, 0); | |
889 } | |
890 | |
891 void WebFrameImpl::setMarkedText( | |
892 const WebString& text, unsigned location, unsigned length) | |
893 { | |
894 Editor* editor = frame()->editor(); | |
895 | |
896 editor->confirmComposition(text); | |
897 | |
898 Vector<CompositionUnderline> decorations; | |
899 editor->setComposition(text, decorations, location, length); | |
900 } | |
901 | |
902 void WebFrameImpl::unmarkText() | |
903 { | |
904 frame()->editor()->confirmCompositionWithoutDisturbingSelection(); | |
905 } | |
906 | |
907 bool WebFrameImpl::hasMarkedText() const | |
908 { | |
909 return frame()->editor()->hasComposition(); | |
910 } | |
911 | |
912 WebRange WebFrameImpl::markedRange() const | |
913 { | |
914 return frame()->editor()->compositionRange(); | |
915 } | |
916 | |
917 bool WebFrameImpl::executeCommand(const WebString& name) | |
918 { | |
919 ASSERT(frame()); | |
920 | |
921 if (name.length() <= 2) | |
922 return false; | |
923 | |
924 // Since we don't have NSControl, we will convert the format of command | |
925 // string and call the function on Editor directly. | |
926 String command = name; | |
927 | |
928 // Make sure the first letter is upper case. | |
929 command.replace(0, 1, command.substring(0, 1).upper()); | |
930 | |
931 // Remove the trailing ':' if existing. | |
932 if (command[command.length() - 1] == UChar(':')) | |
933 command = command.substring(0, command.length() - 1); | |
934 | |
935 bool rv = true; | |
936 | |
937 // Specially handling commands that Editor::execCommand does not directly | |
938 // support. | |
939 if (command == "DeleteToEndOfParagraph") { | |
940 Editor* editor = frame()->editor(); | |
941 if (!editor->deleteWithDirection(SelectionController::FORWARD, | |
942 ParagraphBoundary, | |
943 true, | |
944 false)) { | |
945 editor->deleteWithDirection(SelectionController::FORWARD, | |
946 CharacterGranularity, | |
947 true, | |
948 false); | |
949 } | |
950 } else if (command == "Indent") | |
951 frame()->editor()->indent(); | |
952 else if (command == "Outdent") | |
953 frame()->editor()->outdent(); | |
954 else if (command == "DeleteBackward") | |
955 rv = frame()->editor()->command(AtomicString("BackwardDelete")).execute(); | |
956 else if (command == "DeleteForward") | |
957 rv = frame()->editor()->command(AtomicString("ForwardDelete")).execute(); | |
958 else if (command == "AdvanceToNextMisspelling") { | |
959 // False must be passed here, or the currently selected word will never be | |
960 // skipped. | |
961 frame()->editor()->advanceToNextMisspelling(false); | |
962 } else if (command == "ToggleSpellPanel") | |
963 frame()->editor()->showSpellingGuessPanel(); | |
964 else | |
965 rv = frame()->editor()->command(command).execute(); | |
966 return rv; | |
967 } | |
968 | |
969 bool WebFrameImpl::executeCommand(const WebString& name, const WebString& value) | |
970 { | |
971 ASSERT(frame()); | |
972 String webName = name; | |
973 | |
974 // moveToBeginningOfDocument and moveToEndfDocument are only handled by WebKit | |
975 // for editable nodes. | |
976 if (!frame()->editor()->canEdit() && webName == "moveToBeginningOfDocument") | |
977 return viewImpl()->propagateScroll(ScrollUp, ScrollByDocument); | |
978 | |
979 if (!frame()->editor()->canEdit() && webName == "moveToEndOfDocument") | |
980 return viewImpl()->propagateScroll(ScrollDown, ScrollByDocument); | |
981 | |
982 return frame()->editor()->command(webName).execute(value); | |
983 } | |
984 | |
985 bool WebFrameImpl::isCommandEnabled(const WebString& name) const | |
986 { | |
987 ASSERT(frame()); | |
988 return frame()->editor()->command(name).isEnabled(); | |
989 } | |
990 | |
991 void WebFrameImpl::enableContinuousSpellChecking(bool enable) | |
992 { | |
993 if (enable == isContinuousSpellCheckingEnabled()) | |
994 return; | |
995 frame()->editor()->toggleContinuousSpellChecking(); | |
996 } | |
997 | |
998 bool WebFrameImpl::isContinuousSpellCheckingEnabled() const | |
999 { | |
1000 return frame()->editor()->isContinuousSpellCheckingEnabled(); | |
1001 } | |
1002 | |
1003 bool WebFrameImpl::hasSelection() const | |
1004 { | |
1005 // frame()->selection()->isNone() never returns true. | |
1006 return (frame()->selection()->start() != frame()->selection()->end()); | |
1007 } | |
1008 | |
1009 WebRange WebFrameImpl::selectionRange() const | |
1010 { | |
1011 return frame()->selection()->toNormalizedRange(); | |
1012 } | |
1013 | |
1014 WebString WebFrameImpl::selectionAsText() const | |
1015 { | |
1016 RefPtr<Range> range = frame()->selection()->toNormalizedRange(); | |
1017 if (!range.get()) | |
1018 return WebString(); | |
1019 | |
1020 String text = range->text(); | |
1021 #if PLATFORM(WIN_OS) | |
1022 replaceNewlinesWithWindowsStyleNewlines(text); | |
1023 #endif | |
1024 replaceNBSPWithSpace(text); | |
1025 return text; | |
1026 } | |
1027 | |
1028 WebString WebFrameImpl::selectionAsMarkup() const | |
1029 { | |
1030 RefPtr<Range> range = frame()->selection()->toNormalizedRange(); | |
1031 if (!range.get()) | |
1032 return WebString(); | |
1033 | |
1034 return createMarkup(range.get(), 0); | |
1035 } | |
1036 | |
1037 int WebFrameImpl::printBegin(const WebSize& pageSize) | |
1038 { | |
1039 ASSERT(!frame()->document()->isFrameSet()); | |
1040 | |
1041 m_printContext.set(new ChromePrintContext(frame())); | |
1042 FloatRect rect(0, 0, static_cast<float>(pageSize.width), | |
1043 static_cast<float>(pageSize.height)); | |
1044 m_printContext->begin(rect.width()); | |
1045 float pageHeight; | |
1046 // We ignore the overlays calculation for now since they are generated in the | |
1047 // browser. pageHeight is actually an output parameter. | |
1048 m_printContext->computePageRects(rect, 0, 0, 1.0, pageHeight); | |
1049 return m_printContext->pageCount(); | |
1050 } | |
1051 | |
1052 float WebFrameImpl::getPrintPageShrink(int page) | |
1053 { | |
1054 // Ensure correct state. | |
1055 if (!m_printContext.get() || page < 0) { | |
1056 ASSERT_NOT_REACHED(); | |
1057 return 0; | |
1058 } | |
1059 | |
1060 return m_printContext->getPageShrink(page); | |
1061 } | |
1062 | |
1063 float WebFrameImpl::printPage(int page, WebCanvas* canvas) | |
1064 { | |
1065 // Ensure correct state. | |
1066 if (!m_printContext.get() || page < 0 || !frame() || !frame()->document()) { | |
1067 ASSERT_NOT_REACHED(); | |
1068 return 0; | |
1069 } | |
1070 | |
1071 #if PLATFORM(WIN_OS) || PLATFORM(LINUX) || PLATFORM(FREEBSD) | |
1072 PlatformContextSkia context(canvas); | |
1073 GraphicsContext spool(&context); | |
1074 #elif PLATFORM(DARWIN) | |
1075 GraphicsContext spool(canvas); | |
1076 LocalCurrentGraphicsContext localContext(&spool); | |
1077 #endif | |
1078 | |
1079 return m_printContext->spoolPage(spool, page); | |
1080 } | |
1081 | |
1082 void WebFrameImpl::printEnd() | |
1083 { | |
1084 ASSERT(m_printContext.get()); | |
1085 if (m_printContext.get()) | |
1086 m_printContext->end(); | |
1087 m_printContext.clear(); | |
1088 } | |
1089 | |
1090 bool WebFrameImpl::find(int identifier, | |
1091 const WebString& searchText, | |
1092 const WebFindOptions& options, | |
1093 bool wrapWithinFrame, | |
1094 WebRect* selectionRect) | |
1095 { | |
1096 WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl(); | |
1097 | |
1098 if (!options.findNext) | |
1099 frame()->page()->unmarkAllTextMatches(); | |
1100 else | |
1101 setMarkerActive(m_activeMatch.get(), false); // Active match is changing. | |
1102 | |
1103 // Starts the search from the current selection. | |
1104 bool startInSelection = true; | |
1105 | |
1106 // If the user has selected something since the last Find operation we want | |
1107 // to start from there. Otherwise, we start searching from where the last Find | |
1108 // operation left off (either a Find or a FindNext operation). | |
1109 VisibleSelection selection(frame()->selection()->selection()); | |
1110 bool activeSelection = !selection.isNone(); | |
1111 if (!activeSelection && m_activeMatch) { | |
1112 selection = VisibleSelection(m_activeMatch.get()); | |
1113 frame()->selection()->setSelection(selection); | |
1114 } | |
1115 | |
1116 ASSERT(frame() && frame()->view()); | |
1117 bool found = frame()->findString( | |
1118 searchText, options.forward, options.matchCase, wrapWithinFrame, | |
1119 startInSelection); | |
1120 if (found) { | |
1121 // Store which frame was active. This will come in handy later when we | |
1122 // change the active match ordinal below. | |
1123 WebFrameImpl* oldActiveFrame = mainFrameImpl->m_activeMatchFrame; | |
1124 // Set this frame as the active frame (the one with the active highlight). | |
1125 mainFrameImpl->m_activeMatchFrame = this; | |
1126 | |
1127 // We found something, so we can now query the selection for its position. | |
1128 VisibleSelection newSelection(frame()->selection()->selection()); | |
1129 IntRect currSelectionRect; | |
1130 | |
1131 // If we thought we found something, but it couldn't be selected (perhaps | |
1132 // because it was marked -webkit-user-select: none), we can't set it to | |
1133 // be active but we still continue searching. This matches Safari's | |
1134 // behavior, including some oddities when selectable and un-selectable text | |
1135 // are mixed on a page: see https://bugs.webkit.org/show_bug.cgi?id=19127. | |
1136 if (newSelection.isNone() || (newSelection.start() == newSelection.end())) | |
1137 m_activeMatch = 0; | |
1138 else { | |
1139 m_activeMatch = newSelection.toNormalizedRange(); | |
1140 currSelectionRect = m_activeMatch->boundingBox(); | |
1141 setMarkerActive(m_activeMatch.get(), true); // Active. | |
1142 // WebKit draws the highlighting for all matches. | |
1143 executeCommand(WebString::fromUTF8("Unselect")); | |
1144 } | |
1145 | |
1146 if (!options.findNext || activeSelection) { | |
1147 // This is either a Find operation or a Find-next from a new start point | |
1148 // due to a selection, so we set the flag to ask the scoping effort | |
1149 // to find the active rect for us so we can update the ordinal (n of m). | |
1150 m_locatingActiveRect = true; | |
1151 } else { | |
1152 if (oldActiveFrame != this) { | |
1153 // If the active frame has changed it means that we have a multi-frame | |
1154 // page and we just switch to searching in a new frame. Then we just | |
1155 // want to reset the index. | |
1156 if (options.forward) | |
1157 m_activeMatchIndex = 0; | |
1158 else | |
1159 m_activeMatchIndex = m_lastMatchCount - 1; | |
1160 } else { | |
1161 // We are still the active frame, so increment (or decrement) the | |
1162 // |m_activeMatchIndex|, wrapping if needed (on single frame pages). | |
1163 options.forward ? ++m_activeMatchIndex : --m_activeMatchIndex; | |
1164 if (m_activeMatchIndex + 1 > m_lastMatchCount) | |
1165 m_activeMatchIndex = 0; | |
1166 if (m_activeMatchIndex == -1) | |
1167 m_activeMatchIndex = m_lastMatchCount - 1; | |
1168 } | |
1169 if (selectionRect) { | |
1170 WebRect rect = frame()->view()->convertToContainingWindow(currSelectionRect); | |
1171 rect.x -= frameView()->scrollOffset().width(); | |
1172 rect.y -= frameView()->scrollOffset().height(); | |
1173 *selectionRect = rect; | |
1174 | |
1175 reportFindInPageSelection(rect, m_activeMatchIndex + 1, identifier); | |
1176 } | |
1177 } | |
1178 } else { | |
1179 // Nothing was found in this frame. | |
1180 m_activeMatch = 0; | |
1181 | |
1182 // Erase all previous tickmarks and highlighting. | |
1183 invalidateArea(InvalidateAll); | |
1184 } | |
1185 | |
1186 return found; | |
1187 } | |
1188 | |
1189 void WebFrameImpl::stopFinding(bool clearSelection) | |
1190 { | |
1191 if (!clearSelection) | |
1192 setFindEndstateFocusAndSelection(); | |
1193 cancelPendingScopingEffort(); | |
1194 | |
1195 // Remove all markers for matches found and turn off the highlighting. | |
1196 if (!parent()) | |
1197 frame()->document()->removeMarkers(DocumentMarker::TextMatch); | |
1198 frame()->setMarkedTextMatchesAreHighlighted(false); | |
1199 | |
1200 // Let the frame know that we don't want tickmarks or highlighting anymore. | |
1201 invalidateArea(InvalidateAll); | |
1202 } | |
1203 | |
1204 void WebFrameImpl::scopeStringMatches(int identifier, | |
1205 const WebString& searchText, | |
1206 const WebFindOptions& options, | |
1207 bool reset) | |
1208 { | |
1209 if (!shouldScopeMatches(searchText)) | |
1210 return; | |
1211 | |
1212 WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl(); | |
1213 | |
1214 if (reset) { | |
1215 // This is a brand new search, so we need to reset everything. | |
1216 // Scoping is just about to begin. | |
1217 m_scopingComplete = false; | |
1218 // Clear highlighting for this frame. | |
1219 if (frame()->markedTextMatchesAreHighlighted()) | |
1220 frame()->page()->unmarkAllTextMatches(); | |
1221 // Clear the counters from last operation. | |
1222 m_lastMatchCount = 0; | |
1223 m_nextInvalidateAfter = 0; | |
1224 | |
1225 m_resumeScopingFromRange = 0; | |
1226 | |
1227 mainFrameImpl->m_framesScopingCount++; | |
1228 | |
1229 // Now, defer scoping until later to allow find operation to finish quickly. | |
1230 scopeStringMatchesSoon( | |
1231 identifier, | |
1232 searchText, | |
1233 options, | |
1234 false); // false=we just reset, so don't do it again. | |
1235 return; | |
1236 } | |
1237 | |
1238 RefPtr<Range> searchRange(rangeOfContents(frame()->document())); | |
1239 | |
1240 ExceptionCode ec = 0, ec2 = 0; | |
1241 if (m_resumeScopingFromRange.get()) { | |
1242 // This is a continuation of a scoping operation that timed out and didn't | |
1243 // complete last time around, so we should start from where we left off. | |
1244 searchRange->setStart(m_resumeScopingFromRange->startContainer(), | |
1245 m_resumeScopingFromRange->startOffset(ec2) + 1, | |
1246 ec); | |
1247 if (ec || ec2) { | |
1248 if (ec2) // A non-zero |ec| happens when navigating during search. | |
1249 ASSERT_NOT_REACHED(); | |
1250 return; | |
1251 } | |
1252 } | |
1253 | |
1254 // This timeout controls how long we scope before releasing control. This | |
1255 // value does not prevent us from running for longer than this, but it is | |
1256 // periodically checked to see if we have exceeded our allocated time. | |
1257 const double maxScopingDuration = 0.1; // seconds | |
1258 | |
1259 int matchCount = 0; | |
1260 bool timedOut = false; | |
1261 double startTime = currentTime(); | |
1262 do { | |
1263 // Find next occurrence of the search string. | |
1264 // FIXME: (http://b/1088245) This WebKit operation may run for longer | |
1265 // than the timeout value, and is not interruptible as it is currently | |
1266 // written. We may need to rewrite it with interruptibility in mind, or | |
1267 // find an alternative. | |
1268 RefPtr<Range> resultRange(findPlainText(searchRange.get(), | |
1269 searchText, | |
1270 true, | |
1271 options.matchCase)); | |
1272 if (resultRange->collapsed(ec)) { | |
1273 if (!resultRange->startContainer()->isInShadowTree()) | |
1274 break; | |
1275 | |
1276 searchRange = rangeOfContents(frame()->document()); | |
1277 searchRange->setStartAfter( | |
1278 resultRange->startContainer()->shadowAncestorNode(), ec); | |
1279 continue; | |
1280 } | |
1281 | |
1282 // A non-collapsed result range can in some funky whitespace cases still not | |
1283 // advance the range's start position (4509328). Break to avoid infinite | |
1284 // loop. (This function is based on the implementation of | |
1285 // Frame::markAllMatchesForText, which is where this safeguard comes from). | |
1286 VisiblePosition newStart = endVisiblePosition(resultRange.get(), DOWNSTREAM); | |
1287 if (newStart == startVisiblePosition(searchRange.get(), DOWNSTREAM)) | |
1288 break; | |
1289 | |
1290 // Only treat the result as a match if it is visible | |
1291 if (frame()->editor()->insideVisibleArea(resultRange.get())) { | |
1292 ++matchCount; | |
1293 | |
1294 setStart(searchRange.get(), newStart); | |
1295 Node* shadowTreeRoot = searchRange->shadowTreeRootNode(); | |
1296 if (searchRange->collapsed(ec) && shadowTreeRoot) | |
1297 searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec); | |
1298 | |
1299 // Catch a special case where Find found something but doesn't know what | |
1300 // the bounding box for it is. In this case we set the first match we find | |
1301 // as the active rect. | |
1302 IntRect resultBounds = resultRange->boundingBox(); | |
1303 IntRect activeSelectionRect; | |
1304 if (m_locatingActiveRect) { | |
1305 activeSelectionRect = m_activeMatch.get() ? | |
1306 m_activeMatch->boundingBox() : resultBounds; | |
1307 } | |
1308 | |
1309 // If the Find function found a match it will have stored where the | |
1310 // match was found in m_activeSelectionRect on the current frame. If we | |
1311 // find this rect during scoping it means we have found the active | |
1312 // tickmark. | |
1313 bool foundActiveMatch = false; | |
1314 if (m_locatingActiveRect && (activeSelectionRect == resultBounds)) { | |
1315 // We have found the active tickmark frame. | |
1316 mainFrameImpl->m_activeMatchFrame = this; | |
1317 foundActiveMatch = true; | |
1318 // We also know which tickmark is active now. | |
1319 m_activeMatchIndex = matchCount - 1; | |
1320 // To stop looking for the active tickmark, we set this flag. | |
1321 m_locatingActiveRect = false; | |
1322 | |
1323 // Notify browser of new location for the selected rectangle. | |
1324 resultBounds.move(-frameView()->scrollOffset().width(), | |
1325 -frameView()->scrollOffset().height()); | |
1326 reportFindInPageSelection( | |
1327 frame()->view()->convertToContainingWindow(resultBounds), | |
1328 m_activeMatchIndex + 1, | |
1329 identifier); | |
1330 } | |
1331 | |
1332 addMarker(resultRange.get(), foundActiveMatch); | |
1333 } | |
1334 | |
1335 m_resumeScopingFromRange = resultRange; | |
1336 timedOut = (currentTime() - startTime) >= maxScopingDuration; | |
1337 } while (!timedOut); | |
1338 | |
1339 // Remember what we search for last time, so we can skip searching if more | |
1340 // letters are added to the search string (and last outcome was 0). | |
1341 m_lastSearchString = searchText; | |
1342 | |
1343 if (matchCount > 0) { | |
1344 frame()->setMarkedTextMatchesAreHighlighted(true); | |
1345 | |
1346 m_lastMatchCount += matchCount; | |
1347 | |
1348 // Let the mainframe know how much we found during this pass. | |
1349 mainFrameImpl->increaseMatchCount(matchCount, identifier); | |
1350 } | |
1351 | |
1352 if (timedOut) { | |
1353 // If we found anything during this pass, we should redraw. However, we | |
1354 // don't want to spam too much if the page is extremely long, so if we | |
1355 // reach a certain point we start throttling the redraw requests. | |
1356 if (matchCount > 0) | |
1357 invalidateIfNecessary(); | |
1358 | |
1359 // Scoping effort ran out of time, lets ask for another time-slice. | |
1360 scopeStringMatchesSoon( | |
1361 identifier, | |
1362 searchText, | |
1363 options, | |
1364 false); // don't reset. | |
1365 return; // Done for now, resume work later. | |
1366 } | |
1367 | |
1368 // This frame has no further scoping left, so it is done. Other frames might, | |
1369 // of course, continue to scope matches. | |
1370 m_scopingComplete = true; | |
1371 mainFrameImpl->m_framesScopingCount--; | |
1372 | |
1373 // If this is the last frame to finish scoping we need to trigger the final | |
1374 // update to be sent. | |
1375 if (!mainFrameImpl->m_framesScopingCount) | |
1376 mainFrameImpl->increaseMatchCount(0, identifier); | |
1377 | |
1378 // This frame is done, so show any scrollbar tickmarks we haven't drawn yet. | |
1379 invalidateArea(InvalidateScrollbar); | |
1380 } | |
1381 | |
1382 void WebFrameImpl::cancelPendingScopingEffort() | |
1383 { | |
1384 deleteAllValues(m_deferredScopingWork); | |
1385 m_deferredScopingWork.clear(); | |
1386 | |
1387 m_activeMatchIndex = -1; | |
1388 } | |
1389 | |
1390 void WebFrameImpl::increaseMatchCount(int count, int identifier) | |
1391 { | |
1392 // This function should only be called on the mainframe. | |
1393 ASSERT(!parent()); | |
1394 | |
1395 m_totalMatchCount += count; | |
1396 | |
1397 // Update the UI with the latest findings. | |
1398 if (client()) | |
1399 client()->reportFindInPageMatchCount(identifier, m_totalMatchCount, !m_framesScopingCount); | |
1400 } | |
1401 | |
1402 void WebFrameImpl::reportFindInPageSelection(const WebRect& selectionRect, | |
1403 int activeMatchOrdinal, | |
1404 int identifier) | |
1405 { | |
1406 // Update the UI with the latest selection rect. | |
1407 if (client()) | |
1408 client()->reportFindInPageSelection(identifier, ordinalOfFirstMatchForFrame(this) + activeMatchOrdinal, selectionRect); | |
1409 } | |
1410 | |
1411 void WebFrameImpl::resetMatchCount() | |
1412 { | |
1413 m_totalMatchCount = 0; | |
1414 m_framesScopingCount = 0; | |
1415 } | |
1416 | |
1417 WebURL WebFrameImpl::completeURL(const WebString& url) const | |
1418 { | |
1419 if (!m_frame || !m_frame->document()) | |
1420 return WebURL(); | |
1421 | |
1422 return m_frame->document()->completeURL(url); | |
1423 } | |
1424 | |
1425 WebString WebFrameImpl::contentAsText(size_t maxChars) const | |
1426 { | |
1427 if (!m_frame) | |
1428 return WebString(); | |
1429 | |
1430 Vector<UChar> text; | |
1431 frameContentAsPlainText(maxChars, m_frame, &text); | |
1432 return String::adopt(text); | |
1433 } | |
1434 | |
1435 WebString WebFrameImpl::contentAsMarkup() const | |
1436 { | |
1437 return createFullMarkup(m_frame->document()); | |
1438 } | |
1439 | |
1440 // WebFrameImpl public --------------------------------------------------------- | |
1441 | |
1442 int WebFrameImpl::m_liveObjectCount = 0; | |
1443 | |
1444 PassRefPtr<WebFrameImpl> WebFrameImpl::create(WebFrameClient* client) | |
1445 { | |
1446 return adoptRef(new WebFrameImpl(ClientHandle::create(client))); | |
1447 } | |
1448 | |
1449 WebFrameImpl::WebFrameImpl(PassRefPtr<ClientHandle> clientHandle) | |
1450 : m_frameLoaderClient(this) | |
1451 , m_clientHandle(clientHandle) | |
1452 , m_activeMatchFrame(0) | |
1453 , m_activeMatchIndex(-1) | |
1454 , m_locatingActiveRect(false) | |
1455 , m_resumeScopingFromRange(0) | |
1456 , m_lastMatchCount(-1) | |
1457 , m_totalMatchCount(-1) | |
1458 , m_framesScopingCount(-1) | |
1459 , m_scopingComplete(false) | |
1460 , m_nextInvalidateAfter(0) | |
1461 { | |
1462 ChromiumBridge::incrementStatsCounter(webFrameActiveCount); | |
1463 m_liveObjectCount++; | |
1464 } | |
1465 | |
1466 WebFrameImpl::~WebFrameImpl() | |
1467 { | |
1468 ChromiumBridge::decrementStatsCounter(webFrameActiveCount); | |
1469 m_liveObjectCount--; | |
1470 | |
1471 cancelPendingScopingEffort(); | |
1472 clearPasswordListeners(); | |
1473 } | |
1474 | |
1475 void WebFrameImpl::initializeAsMainFrame(WebViewImpl* webViewImpl) | |
1476 { | |
1477 RefPtr<Frame> frame = Frame::create(webViewImpl->page(), 0, &m_frameLoaderClient); | |
1478 m_frame = frame.get(); | |
1479 | |
1480 // Add reference on behalf of FrameLoader. See comments in | |
1481 // WebFrameLoaderClient::frameLoaderDestroyed for more info. | |
1482 ref(); | |
1483 | |
1484 // We must call init() after m_frame is assigned because it is referenced | |
1485 // during init(). | |
1486 m_frame->init(); | |
1487 } | |
1488 | |
1489 PassRefPtr<Frame> WebFrameImpl::createChildFrame( | |
1490 const FrameLoadRequest& request, HTMLFrameOwnerElement* ownerElement) | |
1491 { | |
1492 RefPtr<WebFrameImpl> webframe(adoptRef(new WebFrameImpl(m_clientHandle))); | |
1493 | |
1494 // Add an extra ref on behalf of the Frame/FrameLoader, which references the | |
1495 // WebFrame via the FrameLoaderClient interface. See the comment at the top | |
1496 // of this file for more info. | |
1497 webframe->ref(); | |
1498 | |
1499 RefPtr<Frame> childFrame = Frame::create( | |
1500 m_frame->page(), ownerElement, &webframe->m_frameLoaderClient); | |
1501 webframe->m_frame = childFrame.get(); | |
1502 | |
1503 childFrame->tree()->setName(request.frameName()); | |
1504 | |
1505 m_frame->tree()->appendChild(childFrame); | |
1506 | |
1507 // Frame::init() can trigger onload event in the parent frame, | |
1508 // which may detach this frame and trigger a null-pointer access | |
1509 // in FrameTree::removeChild. Move init() after appendChild call | |
1510 // so that webframe->mFrame is in the tree before triggering | |
1511 // onload event handler. | |
1512 // Because the event handler may set webframe->mFrame to null, | |
1513 // it is necessary to check the value after calling init() and | |
1514 // return without loading URL. | |
1515 // (b:791612) | |
1516 childFrame->init(); // create an empty document | |
1517 if (!childFrame->tree()->parent()) | |
1518 return 0; | |
1519 | |
1520 m_frame->loader()->loadURLIntoChildFrame( | |
1521 request.resourceRequest().url(), | |
1522 request.resourceRequest().httpReferrer(), | |
1523 childFrame.get()); | |
1524 | |
1525 // A synchronous navigation (about:blank) would have already processed | |
1526 // onload, so it is possible for the frame to have already been destroyed by | |
1527 // script in the page. | |
1528 if (!childFrame->tree()->parent()) | |
1529 return 0; | |
1530 | |
1531 return childFrame.release(); | |
1532 } | |
1533 | |
1534 void WebFrameImpl::layout() | |
1535 { | |
1536 // layout this frame | |
1537 FrameView* view = m_frame->view(); | |
1538 if (view) | |
1539 view->layoutIfNeededRecursive(); | |
1540 } | |
1541 | |
1542 void WebFrameImpl::paint(WebCanvas* canvas, const WebRect& rect) | |
1543 { | |
1544 if (rect.isEmpty()) | |
1545 return; | |
1546 IntRect dirtyRect(rect); | |
1547 #if WEBKIT_USING_CG | |
1548 GraphicsContext gc(canvas); | |
1549 LocalCurrentGraphicsContext localContext(&gc); | |
1550 #elif WEBKIT_USING_SKIA | |
1551 PlatformContextSkia context(canvas); | |
1552 | |
1553 // PlatformGraphicsContext is actually a pointer to PlatformContextSkia | |
1554 GraphicsContext gc(reinterpret_cast<PlatformGraphicsContext*>(&context)); | |
1555 #else | |
1556 notImplemented(); | |
1557 #endif | |
1558 gc.save(); | |
1559 if (m_frame->document() && frameView()) { | |
1560 gc.clip(dirtyRect); | |
1561 frameView()->paint(&gc, dirtyRect); | |
1562 m_frame->page()->inspectorController()->drawNodeHighlight(gc); | |
1563 } else | |
1564 gc.fillRect(dirtyRect, Color::white, DeviceColorSpace); | |
1565 gc.restore(); | |
1566 } | |
1567 | |
1568 void WebFrameImpl::createFrameView() | |
1569 { | |
1570 ASSERT(m_frame); // If m_frame doesn't exist, we probably didn't init properly. | |
1571 | |
1572 Page* page = m_frame->page(); | |
1573 ASSERT(page); | |
1574 ASSERT(page->mainFrame()); | |
1575 | |
1576 bool isMainFrame = m_frame == page->mainFrame(); | |
1577 if (isMainFrame && m_frame->view()) | |
1578 m_frame->view()->setParentVisible(false); | |
1579 | |
1580 m_frame->setView(0); | |
1581 | |
1582 WebViewImpl* webView = viewImpl(); | |
1583 | |
1584 RefPtr<FrameView> view; | |
1585 if (isMainFrame) | |
1586 view = FrameView::create(m_frame, webView->size()); | |
1587 else | |
1588 view = FrameView::create(m_frame); | |
1589 | |
1590 m_frame->setView(view); | |
1591 | |
1592 if (webView->isTransparent()) | |
1593 view->setTransparent(true); | |
1594 | |
1595 // FIXME: The Mac code has a comment about this possibly being unnecessary. | |
1596 // See installInFrame in WebCoreFrameBridge.mm | |
1597 if (m_frame->ownerRenderer()) | |
1598 m_frame->ownerRenderer()->setWidget(view.get()); | |
1599 | |
1600 if (HTMLFrameOwnerElement* owner = m_frame->ownerElement()) | |
1601 view->setCanHaveScrollbars(owner->scrollingMode() != ScrollbarAlwaysOff); | |
1602 | |
1603 if (isMainFrame) | |
1604 view->setParentVisible(true); | |
1605 } | |
1606 | |
1607 WebFrameImpl* WebFrameImpl::fromFrame(Frame* frame) | |
1608 { | |
1609 if (!frame) | |
1610 return 0; | |
1611 | |
1612 return static_cast<FrameLoaderClientImpl*>(frame->loader()->client())->webFrame(); | |
1613 } | |
1614 | |
1615 WebViewImpl* WebFrameImpl::viewImpl() const | |
1616 { | |
1617 if (!m_frame) | |
1618 return 0; | |
1619 | |
1620 return WebViewImpl::fromPage(m_frame->page()); | |
1621 } | |
1622 | |
1623 WebDataSourceImpl* WebFrameImpl::dataSourceImpl() const | |
1624 { | |
1625 return static_cast<WebDataSourceImpl*>(dataSource()); | |
1626 } | |
1627 | |
1628 WebDataSourceImpl* WebFrameImpl::provisionalDataSourceImpl() const | |
1629 { | |
1630 return static_cast<WebDataSourceImpl*>(provisionalDataSource()); | |
1631 } | |
1632 | |
1633 void WebFrameImpl::setFindEndstateFocusAndSelection() | |
1634 { | |
1635 WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl(); | |
1636 | |
1637 if (this == mainFrameImpl->activeMatchFrame() && m_activeMatch.get()) { | |
1638 // If the user has set the selection since the match was found, we | |
1639 // don't focus anything. | |
1640 VisibleSelection selection(frame()->selection()->selection()); | |
1641 if (!selection.isNone()) | |
1642 return; | |
1643 | |
1644 // Try to find the first focusable node up the chain, which will, for | |
1645 // example, focus links if we have found text within the link. | |
1646 Node* node = m_activeMatch->firstNode(); | |
1647 while (node && !node->isFocusable() && node != frame()->document()) | |
1648 node = node->parent(); | |
1649 | |
1650 if (node && node != frame()->document()) { | |
1651 // Found a focusable parent node. Set focus to it. | |
1652 frame()->document()->setFocusedNode(node); | |
1653 } else { | |
1654 // Iterate over all the nodes in the range until we find a focusable node. | |
1655 // This, for example, sets focus to the first link if you search for | |
1656 // text and text that is within one or more links. | |
1657 node = m_activeMatch->firstNode(); | |
1658 while (node && node != m_activeMatch->pastLastNode()) { | |
1659 if (node->isFocusable()) { | |
1660 frame()->document()->setFocusedNode(node); | |
1661 break; | |
1662 } | |
1663 node = node->traverseNextNode(); | |
1664 } | |
1665 } | |
1666 } | |
1667 } | |
1668 | |
1669 void WebFrameImpl::didFail(const ResourceError& error, bool wasProvisional) | |
1670 { | |
1671 if (!client()) | |
1672 return; | |
1673 WebURLError webError = error; | |
1674 if (wasProvisional) | |
1675 client()->didFailProvisionalLoad(this, webError); | |
1676 else | |
1677 client()->didFailLoad(this, webError); | |
1678 } | |
1679 | |
1680 void WebFrameImpl::setAllowsScrolling(bool flag) | |
1681 { | |
1682 m_frame->view()->setCanHaveScrollbars(flag); | |
1683 } | |
1684 | |
1685 void WebFrameImpl::registerPasswordListener( | |
1686 WebInputElement inputElement, | |
1687 WebPasswordAutocompleteListener* listener) | |
1688 { | |
1689 RefPtr<HTMLInputElement> element = inputElement.operator PassRefPtr<HTMLInputElement>(); | |
1690 ASSERT(m_passwordListeners.find(element) == m_passwordListeners.end()); | |
1691 m_passwordListeners.set(element, listener); | |
1692 } | |
1693 | |
1694 WebPasswordAutocompleteListener* WebFrameImpl::getPasswordListener( | |
1695 HTMLInputElement* inputElement) | |
1696 { | |
1697 return m_passwordListeners.get(RefPtr<HTMLInputElement>(inputElement)); | |
1698 } | |
1699 | |
1700 // WebFrameImpl private -------------------------------------------------------- | |
1701 | |
1702 void WebFrameImpl::closing() | |
1703 { | |
1704 m_frame = 0; | |
1705 } | |
1706 | |
1707 void WebFrameImpl::invalidateArea(AreaToInvalidate area) | |
1708 { | |
1709 ASSERT(frame() && frame()->view()); | |
1710 FrameView* view = frame()->view(); | |
1711 | |
1712 if ((area & InvalidateAll) == InvalidateAll) | |
1713 view->invalidateRect(view->frameRect()); | |
1714 else { | |
1715 if ((area & InvalidateContentArea) == InvalidateContentArea) { | |
1716 IntRect contentArea( | |
1717 view->x(), view->y(), view->visibleWidth(), view->visibleHeight()); | |
1718 view->invalidateRect(contentArea); | |
1719 } | |
1720 | |
1721 if ((area & InvalidateScrollbar) == InvalidateScrollbar) { | |
1722 // Invalidate the vertical scroll bar region for the view. | |
1723 IntRect scrollBarVert( | |
1724 view->x() + view->visibleWidth(), view->y(), | |
1725 ScrollbarTheme::nativeTheme()->scrollbarThickness(), | |
1726 view->visibleHeight()); | |
1727 view->invalidateRect(scrollBarVert); | |
1728 } | |
1729 } | |
1730 } | |
1731 | |
1732 void WebFrameImpl::addMarker(Range* range, bool activeMatch) | |
1733 { | |
1734 // Use a TextIterator to visit the potentially multiple nodes the range | |
1735 // covers. | |
1736 TextIterator markedText(range); | |
1737 for (; !markedText.atEnd(); markedText.advance()) { | |
1738 RefPtr<Range> textPiece = markedText.range(); | |
1739 int exception = 0; | |
1740 | |
1741 DocumentMarker marker = { | |
1742 DocumentMarker::TextMatch, | |
1743 textPiece->startOffset(exception), | |
1744 textPiece->endOffset(exception), | |
1745 "", | |
1746 activeMatch | |
1747 }; | |
1748 | |
1749 if (marker.endOffset > marker.startOffset) { | |
1750 // Find the node to add a marker to and add it. | |
1751 Node* node = textPiece->startContainer(exception); | |
1752 frame()->document()->addMarker(node, marker); | |
1753 | |
1754 // Rendered rects for markers in WebKit are not populated until each time | |
1755 // the markers are painted. However, we need it to happen sooner, because | |
1756 // the whole purpose of tickmarks on the scrollbar is to show where | |
1757 // matches off-screen are (that haven't been painted yet). | |
1758 Vector<DocumentMarker> markers = frame()->document()->markersForNode(node); | |
1759 frame()->document()->setRenderedRectForMarker( | |
1760 textPiece->startContainer(exception), | |
1761 markers[markers.size() - 1], | |
1762 range->boundingBox()); | |
1763 } | |
1764 } | |
1765 } | |
1766 | |
1767 void WebFrameImpl::setMarkerActive(Range* range, bool active) | |
1768 { | |
1769 if (!range) | |
1770 return; | |
1771 | |
1772 frame()->document()->setMarkersActive(range, active); | |
1773 } | |
1774 | |
1775 int WebFrameImpl::ordinalOfFirstMatchForFrame(WebFrameImpl* frame) const | |
1776 { | |
1777 int ordinal = 0; | |
1778 WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl(); | |
1779 // Iterate from the main frame up to (but not including) |frame| and | |
1780 // add up the number of matches found so far. | |
1781 for (WebFrameImpl* it = mainFrameImpl; | |
1782 it != frame; | |
1783 it = static_cast<WebFrameImpl*>(it->traverseNext(true))) { | |
1784 if (it->m_lastMatchCount > 0) | |
1785 ordinal += it->m_lastMatchCount; | |
1786 } | |
1787 return ordinal; | |
1788 } | |
1789 | |
1790 bool WebFrameImpl::shouldScopeMatches(const String& searchText) | |
1791 { | |
1792 // Don't scope if we can't find a frame or if the frame is not visible. | |
1793 // The user may have closed the tab/application, so abort. | |
1794 if (!frame() || !hasVisibleContent()) | |
1795 return false; | |
1796 | |
1797 ASSERT(frame()->document() && frame()->view()); | |
1798 | |
1799 // If the frame completed the scoping operation and found 0 matches the last | |
1800 // time it was searched, then we don't have to search it again if the user is | |
1801 // just adding to the search string or sending the same search string again. | |
1802 if (m_scopingComplete && !m_lastSearchString.isEmpty() && !m_lastMatchCount) { | |
1803 // Check to see if the search string prefixes match. | |
1804 String previousSearchPrefix = | |
1805 searchText.substring(0, m_lastSearchString.length()); | |
1806 | |
1807 if (previousSearchPrefix == m_lastSearchString) | |
1808 return false; // Don't search this frame, it will be fruitless. | |
1809 } | |
1810 | |
1811 return true; | |
1812 } | |
1813 | |
1814 void WebFrameImpl::scopeStringMatchesSoon(int identifier, const WebString& searchText, | |
1815 const WebFindOptions& options, bool reset) | |
1816 { | |
1817 m_deferredScopingWork.append(new DeferredScopeStringMatches( | |
1818 this, identifier, searchText, options, reset)); | |
1819 } | |
1820 | |
1821 void WebFrameImpl::callScopeStringMatches(DeferredScopeStringMatches* caller, | |
1822 int identifier, const WebString& searchText, | |
1823 const WebFindOptions& options, bool reset) | |
1824 { | |
1825 m_deferredScopingWork.remove(m_deferredScopingWork.find(caller)); | |
1826 | |
1827 scopeStringMatches(identifier, searchText, options, reset); | |
1828 | |
1829 // This needs to happen last since searchText is passed by reference. | |
1830 delete caller; | |
1831 } | |
1832 | |
1833 void WebFrameImpl::invalidateIfNecessary() | |
1834 { | |
1835 if (m_lastMatchCount > m_nextInvalidateAfter) { | |
1836 // FIXME: (http://b/1088165) Optimize the drawing of the tickmarks and | |
1837 // remove this. This calculation sets a milestone for when next to | |
1838 // invalidate the scrollbar and the content area. We do this so that we | |
1839 // don't spend too much time drawing the scrollbar over and over again. | |
1840 // Basically, up until the first 500 matches there is no throttle. | |
1841 // After the first 500 matches, we set set the milestone further and | |
1842 // further out (750, 1125, 1688, 2K, 3K). | |
1843 static const int startSlowingDownAfter = 500; | |
1844 static const int slowdown = 750; | |
1845 int i = (m_lastMatchCount / startSlowingDownAfter); | |
1846 m_nextInvalidateAfter += i * slowdown; | |
1847 | |
1848 invalidateArea(InvalidateScrollbar); | |
1849 } | |
1850 } | |
1851 | |
1852 void WebFrameImpl::clearPasswordListeners() | |
1853 { | |
1854 deleteAllValues(m_passwordListeners); | |
1855 m_passwordListeners.clear(); | |
1856 } | |
1857 | |
1858 void WebFrameImpl::loadJavaScriptURL(const KURL& url) | |
1859 { | |
1860 // This is copied from FrameLoader::executeIfJavaScriptURL. Unfortunately, | |
1861 // we cannot just use that method since it is private, and it also doesn't | |
1862 // quite behave as we require it to for bookmarklets. The key difference is | |
1863 // that we need to suppress loading the string result from evaluating the JS | |
1864 // URL if executing the JS URL resulted in a location change. We also allow | |
1865 // a JS URL to be loaded even if scripts on the page are otherwise disabled. | |
1866 | |
1867 if (!m_frame->document() || !m_frame->page()) | |
1868 return; | |
1869 | |
1870 String script = decodeURLEscapeSequences(url.string().substring(strlen("javascript:"))); | |
1871 ScriptValue result = m_frame->script()->executeScript(script, true); | |
1872 | |
1873 String scriptResult; | |
1874 if (!result.getString(scriptResult)) | |
1875 return; | |
1876 | |
1877 SecurityOrigin* securityOrigin = m_frame->document()->securityOrigin(); | |
1878 | |
1879 if (!m_frame->redirectScheduler()->locationChangePending()) { | |
1880 m_frame->loader()->stopAllLoaders(); | |
1881 m_frame->loader()->begin(m_frame->loader()->url(), true, securityOrigin); | |
1882 m_frame->loader()->write(scriptResult); | |
1883 m_frame->loader()->end(); | |
1884 } | |
1885 } | |
1886 | |
1887 } // namespace WebKit | |
OLD | NEW |