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 "public/platform/Platform.h" | |
75 #include "public/platform/WebFileSystem.h" | |
76 #include "public/platform/WebFileSystemType.h" | |
77 #include "public/platform/WebFloatPoint.h" | |
78 #include "public/platform/WebFloatRect.h" | |
79 #include "public/platform/WebPoint.h" | |
80 #include "public/platform/WebRect.h" | |
81 #include "public/platform/WebSize.h" | |
82 #include "public/platform/WebURLError.h" | |
83 #include "public/platform/WebVector.h" | |
84 #include "wtf/CurrentTime.h" | |
85 #include "wtf/HashMap.h" | |
86 #include <algorithm> | |
87 #include "AssociatedURLLoader.h" | |
88 #include "AsyncFileSystemChromium.h" | |
89 #include "DOMUtilitiesPrivate.h" | |
90 #include "EventListenerWrapper.h" | |
91 #include "FindInPageCoordinates.h" | |
92 #include "HTMLNames.h" | |
93 #include "PageOverlay.h" | |
94 #include "V8DOMFileSystem.h" | |
95 #include "V8DirectoryEntry.h" | |
96 #include "V8FileEntry.h" | |
97 #include "WebConsoleMessage.h" | |
98 #include "WebDOMEvent.h" | |
99 #include "WebDOMEventListener.h" | |
100 #include "WebDataSourceImpl.h" | |
101 #include "WebDevToolsAgentPrivate.h" | |
102 #include "WebDocument.h" | |
103 #include "WebFindOptions.h" | |
104 #include "WebFormElement.h" | |
105 #include "WebFrameClient.h" | |
106 #include "WebHistoryItem.h" | |
107 #include "WebIconURL.h" | |
108 #include "WebInputElement.h" | |
109 #include "WebNode.h" | |
110 #include "WebPerformance.h" | |
111 #include "WebPlugin.h" | |
112 #include "WebPluginContainerImpl.h" | |
113 #include "WebPrintParams.h" | |
114 #include "WebRange.h" | |
115 #include "WebScriptSource.h" | |
116 #include "WebSecurityOrigin.h" | |
117 #include "WebSerializedScriptValue.h" | |
118 #include "WebViewImpl.h" | |
119 #include "bindings/v8/DOMWrapperWorld.h" | |
120 #include "bindings/v8/ScriptController.h" | |
121 #include "bindings/v8/ScriptSourceCode.h" | |
122 #include "bindings/v8/ScriptValue.h" | |
123 #include "bindings/v8/V8GCController.h" | |
124 #include "core/dom/Document.h" | |
125 #include "core/dom/DocumentMarker.h" | |
126 #include "core/dom/DocumentMarkerController.h" | |
127 #include "core/dom/IconURL.h" | |
128 #include "core/dom/MessagePort.h" | |
129 #include "core/dom/Node.h" | |
130 #include "core/dom/NodeTraversal.h" | |
131 #include "core/dom/UserGestureIndicator.h" | |
132 #include "core/dom/default/chromium/PlatformMessagePortChannelChromium.h" | |
133 #include "core/dom/shadow/ShadowRoot.h" | |
134 #include "core/editing/Editor.h" | |
135 #include "core/editing/FrameSelection.h" | |
136 #include "core/editing/SpellChecker.h" | |
137 #include "core/editing/TextAffinity.h" | |
138 #include "core/editing/TextIterator.h" | |
139 #include "core/editing/htmlediting.h" | |
140 #include "core/editing/markup.h" | |
141 #include "core/history/BackForwardController.h" | |
142 #include "core/history/HistoryItem.h" | |
143 #include "core/html/HTMLCollection.h" | |
144 #include "core/html/HTMLFormElement.h" | |
145 #include "core/html/HTMLFrameOwnerElement.h" | |
146 #include "core/html/HTMLHeadElement.h" | |
147 #include "core/html/HTMLInputElement.h" | |
148 #include "core/html/HTMLLinkElement.h" | |
149 #include "core/html/PluginDocument.h" | |
150 #include "core/inspector/InspectorController.h" | |
151 #include "core/inspector/ScriptCallStack.h" | |
152 #include "core/loader/DocumentLoader.h" | |
153 #include "core/loader/FormState.h" | |
154 #include "core/loader/FrameLoadRequest.h" | |
155 #include "core/loader/FrameLoader.h" | |
156 #include "core/loader/IconController.h" | |
157 #include "core/loader/SubstituteData.h" | |
158 #include "core/page/Chrome.h" | |
159 #include "core/page/Console.h" | |
160 #include "core/page/DOMWindow.h" | |
161 #include "core/page/EventHandler.h" | |
162 #include "core/page/FocusController.h" | |
163 #include "core/page/FrameTree.h" | |
164 #include "core/page/FrameView.h" | |
165 #include "core/page/Page.h" | |
166 #include "core/page/Performance.h" | |
167 #include "core/page/PrintContext.h" | |
168 #include "core/page/Settings.h" | |
169 #include "core/platform/AsyncFileSystem.h" | |
170 #include "core/platform/ScrollTypes.h" | |
171 #include "core/platform/ScrollbarTheme.h" | |
172 #include "core/platform/chromium/ClipboardUtilitiesChromium.h" | |
173 #include "core/platform/chromium/TraceEvent.h" | |
174 #include "core/platform/graphics/FontCache.h" | |
175 #include "core/platform/graphics/GraphicsContext.h" | |
176 #include "core/platform/graphics/GraphicsLayerClient.h" | |
177 #include "core/platform/graphics/skia/SkiaUtils.h" | |
178 #include "core/platform/network/ResourceHandle.h" | |
179 #include "core/platform/network/ResourceRequest.h" | |
180 #include "core/rendering/HitTestResult.h" | |
181 #include "core/rendering/RenderBox.h" | |
182 #include "core/rendering/RenderFrame.h" | |
183 #include "core/rendering/RenderLayer.h" | |
184 #include "core/rendering/RenderObject.h" | |
185 #include "core/rendering/RenderTreeAsText.h" | |
186 #include "core/rendering/RenderView.h" | |
187 #include "core/rendering/style/StyleInheritedData.h" | |
188 #include "core/xml/XPathResult.h" | |
189 #include "modules/filesystem/DOMFileSystem.h" | |
190 #include "modules/filesystem/DirectoryEntry.h" | |
191 #include "modules/filesystem/FileEntry.h" | |
192 #include "modules/filesystem/FileSystemType.h" | |
193 #include "weborigin/KURL.h" | |
194 #include "weborigin/SchemeRegistry.h" | |
195 #include "weborigin/SecurityPolicy.h" | |
196 | |
197 using namespace WebCore; | |
198 | |
199 namespace WebKit { | |
200 | |
201 static int frameCount = 0; | |
202 | |
203 // Key for a StatsCounter tracking how many WebFrames are active. | |
204 static const char* const webFrameActiveCount = "WebFrameActiveCount"; | |
205 | |
206 // Backend for contentAsPlainText, this is a recursive function that gets | |
207 // the text for the current frame and all of its subframes. It will append | |
208 // the text of each frame in turn to the |output| up to |maxChars| length. | |
209 // | |
210 // The |frame| must be non-null. | |
211 // | |
212 // FIXME: We should use StringBuilder rather than Vector<UChar>. | |
213 static void frameContentAsPlainText(size_t maxChars, Frame* frame, Vector<UChar>
* output) | |
214 { | |
215 Document* document = frame->document(); | |
216 if (!document) | |
217 return; | |
218 | |
219 if (!frame->view()) | |
220 return; | |
221 | |
222 // TextIterator iterates over the visual representation of the DOM. As such, | |
223 // it requires you to do a layout before using it (otherwise it'll crash). | |
224 if (frame->view()->needsLayout()) | |
225 frame->view()->layout(); | |
226 | |
227 // Select the document body. | |
228 RefPtr<Range> range(document->createRange()); | |
229 ExceptionCode exception = 0; | |
230 range->selectNodeContents(document->body(), exception); | |
231 | |
232 if (!exception) { | |
233 // The text iterator will walk nodes giving us text. This is similar to | |
234 // the plainText() function in core/editing/TextIterator.h, but we imple
ment the maximum | |
235 // size and also copy the results directly into a wstring, avoiding the | |
236 // string conversion. | |
237 for (TextIterator it(range.get()); !it.atEnd(); it.advance()) { | |
238 const UChar* chars = it.characters(); | |
239 if (!chars) { | |
240 if (it.length()) { | |
241 // It appears from crash reports that an iterator can get in
to a state | |
242 // where the character count is nonempty but the character p
ointer is | |
243 // null. advance()ing it will then just add that many to the
null | |
244 // pointer which won't be caught in a null check but will cr
ash. | |
245 // | |
246 // A null pointer and 0 length is common for some nodes. | |
247 // | |
248 // IF YOU CATCH THIS IN A DEBUGGER please let brettw know. W
e don't | |
249 // currently understand the conditions for this to occur. Id
eally, the | |
250 // iterators would never get into the condition so we should
fix them | |
251 // if we can. | |
252 ASSERT_NOT_REACHED(); | |
253 break; | |
254 } | |
255 | |
256 // Just got a null node, we can forge ahead! | |
257 continue; | |
258 } | |
259 size_t toAppend = | |
260 std::min(static_cast<size_t>(it.length()), maxChars - output->si
ze()); | |
261 output->append(chars, toAppend); | |
262 if (output->size() >= maxChars) | |
263 return; // Filled up the buffer. | |
264 } | |
265 } | |
266 | |
267 // The separator between frames when the frames are converted to plain text. | |
268 const UChar frameSeparator[] = { '\n', '\n' }; | |
269 const size_t frameSeparatorLen = 2; | |
270 | |
271 // Recursively walk the children. | |
272 FrameTree* frameTree = frame->tree(); | |
273 for (Frame* curChild = frameTree->firstChild(); curChild; curChild = curChil
d->tree()->nextSibling()) { | |
274 // Ignore the text of non-visible frames. | |
275 RenderView* contentRenderer = curChild->contentRenderer(); | |
276 RenderPart* ownerRenderer = curChild->ownerRenderer(); | |
277 if (!contentRenderer || !contentRenderer->width() || !contentRenderer->h
eight() | |
278 || (contentRenderer->x() + contentRenderer->width() <= 0) || (conten
tRenderer->y() + contentRenderer->height() <= 0) | |
279 || (ownerRenderer && ownerRenderer->style() && ownerRenderer->style(
)->visibility() != VISIBLE)) { | |
280 continue; | |
281 } | |
282 | |
283 // Make sure the frame separator won't fill up the buffer, and give up i
f | |
284 // it will. The danger is if the separator will make the buffer longer t
han | |
285 // maxChars. This will cause the computation above: | |
286 // maxChars - output->size() | |
287 // to be a negative number which will crash when the subframe is added. | |
288 if (output->size() >= maxChars - frameSeparatorLen) | |
289 return; | |
290 | |
291 output->append(frameSeparator, frameSeparatorLen); | |
292 frameContentAsPlainText(maxChars, curChild, output); | |
293 if (output->size() >= maxChars) | |
294 return; // Filled up the buffer. | |
295 } | |
296 } | |
297 | |
298 static long long generateFrameIdentifier() | |
299 { | |
300 static long long next = 0; | |
301 return ++next; | |
302 } | |
303 | |
304 WebPluginContainerImpl* WebFrameImpl::pluginContainerFromFrame(Frame* frame) | |
305 { | |
306 if (!frame) | |
307 return 0; | |
308 if (!frame->document() || !frame->document()->isPluginDocument()) | |
309 return 0; | |
310 PluginDocument* pluginDocument = static_cast<PluginDocument*>(frame->documen
t()); | |
311 return static_cast<WebPluginContainerImpl *>(pluginDocument->pluginWidget())
; | |
312 } | |
313 | |
314 WebPluginContainerImpl* WebFrameImpl::pluginContainerFromNode(WebCore::Frame* fr
ame, const WebNode& node) | |
315 { | |
316 WebPluginContainerImpl* pluginContainer = pluginContainerFromFrame(frame); | |
317 if (pluginContainer) | |
318 return pluginContainer; | |
319 return static_cast<WebPluginContainerImpl*>(node.pluginContainer()); | |
320 } | |
321 | |
322 // Simple class to override some of PrintContext behavior. Some of the methods | |
323 // made virtual so that they can be overridden by ChromePluginPrintContext. | |
324 class ChromePrintContext : public PrintContext { | |
325 WTF_MAKE_NONCOPYABLE(ChromePrintContext); | |
326 public: | |
327 ChromePrintContext(Frame* frame) | |
328 : PrintContext(frame) | |
329 , m_printedPageWidth(0) | |
330 { | |
331 } | |
332 | |
333 virtual ~ChromePrintContext() { } | |
334 | |
335 virtual void begin(float width, float height) | |
336 { | |
337 ASSERT(!m_printedPageWidth); | |
338 m_printedPageWidth = width; | |
339 PrintContext::begin(m_printedPageWidth, height); | |
340 } | |
341 | |
342 virtual void end() | |
343 { | |
344 PrintContext::end(); | |
345 } | |
346 | |
347 virtual float getPageShrink(int pageNumber) const | |
348 { | |
349 IntRect pageRect = m_pageRects[pageNumber]; | |
350 return m_printedPageWidth / pageRect.width(); | |
351 } | |
352 | |
353 // Spools the printed page, a subrect of frame(). Skip the scale step. | |
354 // NativeTheme doesn't play well with scaling. Scaling is done browser side | |
355 // instead. Returns the scale to be applied. | |
356 // On Linux, we don't have the problem with NativeTheme, hence we let WebKit | |
357 // do the scaling and ignore the return value. | |
358 virtual float spoolPage(GraphicsContext& context, int pageNumber) | |
359 { | |
360 IntRect pageRect = m_pageRects[pageNumber]; | |
361 float scale = m_printedPageWidth / pageRect.width(); | |
362 | |
363 context.save(); | |
364 #if OS(UNIX) && !OS(DARWIN) | |
365 context.scale(WebCore::FloatSize(scale, scale)); | |
366 #endif | |
367 context.translate(static_cast<float>(-pageRect.x()), static_cast<float>(
-pageRect.y())); | |
368 context.clip(pageRect); | |
369 frame()->view()->paintContents(&context, pageRect); | |
370 if (context.supportsURLFragments()) | |
371 outputLinkedDestinations(context, frame()->document(), pageRect); | |
372 context.restore(); | |
373 return scale; | |
374 } | |
375 | |
376 void spoolAllPagesWithBoundaries(GraphicsContext& graphicsContext, const Flo
atSize& pageSizeInPixels) | |
377 { | |
378 if (!frame()->document() || !frame()->view() || !frame()->document()->re
nderer()) | |
379 return; | |
380 | |
381 frame()->document()->updateLayout(); | |
382 | |
383 float pageHeight; | |
384 computePageRects(FloatRect(FloatPoint(0, 0), pageSizeInPixels), 0, 0, 1,
pageHeight); | |
385 | |
386 const float pageWidth = pageSizeInPixels.width(); | |
387 size_t numPages = pageRects().size(); | |
388 int totalHeight = numPages * (pageSizeInPixels.height() + 1) - 1; | |
389 | |
390 // Fill the whole background by white. | |
391 graphicsContext.setFillColor(Color::white); | |
392 graphicsContext.fillRect(FloatRect(0, 0, pageWidth, totalHeight)); | |
393 | |
394 graphicsContext.save(); | |
395 | |
396 int currentHeight = 0; | |
397 for (size_t pageIndex = 0; pageIndex < numPages; pageIndex++) { | |
398 // Draw a line for a page boundary if this isn't the first page. | |
399 if (pageIndex > 0) { | |
400 graphicsContext.save(); | |
401 graphicsContext.setStrokeColor(Color(0, 0, 255)); | |
402 graphicsContext.setFillColor(Color(0, 0, 255)); | |
403 graphicsContext.drawLine(IntPoint(0, currentHeight), IntPoint(pa
geWidth, currentHeight)); | |
404 graphicsContext.restore(); | |
405 } | |
406 | |
407 graphicsContext.save(); | |
408 | |
409 graphicsContext.translate(0, currentHeight); | |
410 #if !OS(UNIX) || OS(DARWIN) | |
411 // Account for the disabling of scaling in spoolPage. In the context | |
412 // of spoolAllPagesWithBoundaries the scale HAS NOT been pre-applied
. | |
413 float scale = getPageShrink(pageIndex); | |
414 graphicsContext.scale(WebCore::FloatSize(scale, scale)); | |
415 #endif | |
416 spoolPage(graphicsContext, pageIndex); | |
417 graphicsContext.restore(); | |
418 | |
419 currentHeight += pageSizeInPixels.height() + 1; | |
420 } | |
421 | |
422 graphicsContext.restore(); | |
423 } | |
424 | |
425 virtual void computePageRects(const FloatRect& printRect, float headerHeight
, float footerHeight, float userScaleFactor, float& outPageHeight) | |
426 { | |
427 PrintContext::computePageRects(printRect, headerHeight, footerHeight, us
erScaleFactor, outPageHeight); | |
428 } | |
429 | |
430 virtual int pageCount() const | |
431 { | |
432 return PrintContext::pageCount(); | |
433 } | |
434 | |
435 virtual bool shouldUseBrowserOverlays() const | |
436 { | |
437 return true; | |
438 } | |
439 | |
440 private: | |
441 // Set when printing. | |
442 float m_printedPageWidth; | |
443 }; | |
444 | |
445 // Simple class to override some of PrintContext behavior. This is used when | |
446 // the frame hosts a plugin that supports custom printing. In this case, we | |
447 // want to delegate all printing related calls to the plugin. | |
448 class ChromePluginPrintContext : public ChromePrintContext { | |
449 public: | |
450 ChromePluginPrintContext(Frame* frame, WebPluginContainerImpl* plugin, const
WebPrintParams& printParams) | |
451 : ChromePrintContext(frame), m_plugin(plugin), m_pageCount(0), m_printPa
rams(printParams) | |
452 { | |
453 } | |
454 | |
455 virtual ~ChromePluginPrintContext() { } | |
456 | |
457 virtual void begin(float width, float height) | |
458 { | |
459 } | |
460 | |
461 virtual void end() | |
462 { | |
463 m_plugin->printEnd(); | |
464 } | |
465 | |
466 virtual float getPageShrink(int pageNumber) const | |
467 { | |
468 // We don't shrink the page (maybe we should ask the widget ??) | |
469 return 1.0; | |
470 } | |
471 | |
472 virtual void computePageRects(const FloatRect& printRect, float headerHeight
, float footerHeight, float userScaleFactor, float& outPageHeight) | |
473 { | |
474 m_printParams.printContentArea = IntRect(printRect); | |
475 m_pageCount = m_plugin->printBegin(m_printParams); | |
476 } | |
477 | |
478 virtual int pageCount() const | |
479 { | |
480 return m_pageCount; | |
481 } | |
482 | |
483 // Spools the printed page, a subrect of frame(). Skip the scale step. | |
484 // NativeTheme doesn't play well with scaling. Scaling is done browser side | |
485 // instead. Returns the scale to be applied. | |
486 virtual float spoolPage(GraphicsContext& context, int pageNumber) | |
487 { | |
488 m_plugin->printPage(pageNumber, &context); | |
489 return 1.0; | |
490 } | |
491 | |
492 virtual bool shouldUseBrowserOverlays() const | |
493 { | |
494 return false; | |
495 } | |
496 | |
497 private: | |
498 // Set when printing. | |
499 WebPluginContainerImpl* m_plugin; | |
500 int m_pageCount; | |
501 WebPrintParams m_printParams; | |
502 | |
503 }; | |
504 | |
505 static WebDataSource* DataSourceForDocLoader(DocumentLoader* loader) | |
506 { | |
507 return loader ? WebDataSourceImpl::fromDocumentLoader(loader) : 0; | |
508 } | |
509 | |
510 WebFrameImpl::FindMatch::FindMatch(PassRefPtr<Range> range, int ordinal) | |
511 : m_range(range) | |
512 , m_ordinal(ordinal) | |
513 { | |
514 } | |
515 | |
516 class WebFrameImpl::DeferredScopeStringMatches { | |
517 public: | |
518 DeferredScopeStringMatches(WebFrameImpl* webFrame, int identifier, const Web
String& searchText, const WebFindOptions& options, bool reset) | |
519 : m_timer(this, &DeferredScopeStringMatches::doTimeout) | |
520 , m_webFrame(webFrame) | |
521 , m_identifier(identifier) | |
522 , m_searchText(searchText) | |
523 , m_options(options) | |
524 , m_reset(reset) | |
525 { | |
526 m_timer.startOneShot(0.0); | |
527 } | |
528 | |
529 private: | |
530 void doTimeout(Timer<DeferredScopeStringMatches>*) | |
531 { | |
532 m_webFrame->callScopeStringMatches(this, m_identifier, m_searchText, m_o
ptions, m_reset); | |
533 } | |
534 | |
535 Timer<DeferredScopeStringMatches> m_timer; | |
536 RefPtr<WebFrameImpl> m_webFrame; | |
537 int m_identifier; | |
538 WebString m_searchText; | |
539 WebFindOptions m_options; | |
540 bool m_reset; | |
541 }; | |
542 | |
543 // WebFrame ------------------------------------------------------------------- | |
544 | |
545 int WebFrame::instanceCount() | |
546 { | |
547 return frameCount; | |
548 } | |
549 | |
550 WebFrame* WebFrame::frameForCurrentContext() | |
551 { | |
552 v8::Handle<v8::Context> context = v8::Context::GetCurrent(); | |
553 if (context.IsEmpty()) | |
554 return 0; | |
555 return frameForContext(context); | |
556 } | |
557 | |
558 WebFrame* WebFrame::frameForContext(v8::Handle<v8::Context> context) | |
559 { | |
560 return WebFrameImpl::fromFrame(toFrameIfNotDetached(context)); | |
561 } | |
562 | |
563 WebFrame* WebFrame::fromFrameOwnerElement(const WebElement& element) | |
564 { | |
565 return WebFrameImpl::fromFrameOwnerElement(PassRefPtr<Element>(element).get(
)); | |
566 } | |
567 | |
568 WebString WebFrameImpl::uniqueName() const | |
569 { | |
570 return frame()->tree()->uniqueName(); | |
571 } | |
572 | |
573 WebString WebFrameImpl::assignedName() const | |
574 { | |
575 return frame()->tree()->name(); | |
576 } | |
577 | |
578 void WebFrameImpl::setName(const WebString& name) | |
579 { | |
580 frame()->tree()->setName(name); | |
581 } | |
582 | |
583 long long WebFrameImpl::identifier() const | |
584 { | |
585 return m_identifier; | |
586 } | |
587 | |
588 WebVector<WebIconURL> WebFrameImpl::iconURLs(int iconTypesMask) const | |
589 { | |
590 // The URL to the icon may be in the header. As such, only | |
591 // ask the loader for the icon if it's finished loading. | |
592 if (frame()->loader()->state() == FrameStateComplete) | |
593 return frame()->loader()->icon()->urlsForTypes(iconTypesMask); | |
594 return WebVector<WebIconURL>(); | |
595 } | |
596 | |
597 WebSize WebFrameImpl::scrollOffset() const | |
598 { | |
599 FrameView* view = frameView(); | |
600 if (!view) | |
601 return WebSize(); | |
602 return view->scrollOffset(); | |
603 } | |
604 | |
605 WebSize WebFrameImpl::minimumScrollOffset() const | |
606 { | |
607 FrameView* view = frameView(); | |
608 if (!view) | |
609 return WebSize(); | |
610 return toIntSize(view->minimumScrollPosition()); | |
611 } | |
612 | |
613 WebSize WebFrameImpl::maximumScrollOffset() const | |
614 { | |
615 FrameView* view = frameView(); | |
616 if (!view) | |
617 return WebSize(); | |
618 return toIntSize(view->maximumScrollPosition()); | |
619 } | |
620 | |
621 void WebFrameImpl::setScrollOffset(const WebSize& offset) | |
622 { | |
623 if (FrameView* view = frameView()) | |
624 view->setScrollOffset(IntPoint(offset.width, offset.height)); | |
625 } | |
626 | |
627 WebSize WebFrameImpl::contentsSize() const | |
628 { | |
629 return frame()->view()->contentsSize(); | |
630 } | |
631 | |
632 bool WebFrameImpl::hasVisibleContent() const | |
633 { | |
634 return frame()->view()->visibleWidth() > 0 && frame()->view()->visibleHeight
() > 0; | |
635 } | |
636 | |
637 WebRect WebFrameImpl::visibleContentRect() const | |
638 { | |
639 return frame()->view()->visibleContentRect(); | |
640 } | |
641 | |
642 bool WebFrameImpl::hasHorizontalScrollbar() const | |
643 { | |
644 return frame() && frame()->view() && frame()->view()->horizontalScrollbar(); | |
645 } | |
646 | |
647 bool WebFrameImpl::hasVerticalScrollbar() const | |
648 { | |
649 return frame() && frame()->view() && frame()->view()->verticalScrollbar(); | |
650 } | |
651 | |
652 WebView* WebFrameImpl::view() const | |
653 { | |
654 return viewImpl(); | |
655 } | |
656 | |
657 WebFrame* WebFrameImpl::opener() const | |
658 { | |
659 if (!frame()) | |
660 return 0; | |
661 return fromFrame(frame()->loader()->opener()); | |
662 } | |
663 | |
664 void WebFrameImpl::setOpener(const WebFrame* webFrame) | |
665 { | |
666 frame()->loader()->setOpener(webFrame ? static_cast<const WebFrameImpl*>(web
Frame)->frame() : 0); | |
667 } | |
668 | |
669 WebFrame* WebFrameImpl::parent() const | |
670 { | |
671 if (!frame()) | |
672 return 0; | |
673 return fromFrame(frame()->tree()->parent()); | |
674 } | |
675 | |
676 WebFrame* WebFrameImpl::top() const | |
677 { | |
678 if (!frame()) | |
679 return 0; | |
680 return fromFrame(frame()->tree()->top()); | |
681 } | |
682 | |
683 WebFrame* WebFrameImpl::firstChild() const | |
684 { | |
685 if (!frame()) | |
686 return 0; | |
687 return fromFrame(frame()->tree()->firstChild()); | |
688 } | |
689 | |
690 WebFrame* WebFrameImpl::lastChild() const | |
691 { | |
692 if (!frame()) | |
693 return 0; | |
694 return fromFrame(frame()->tree()->lastChild()); | |
695 } | |
696 | |
697 WebFrame* WebFrameImpl::nextSibling() const | |
698 { | |
699 if (!frame()) | |
700 return 0; | |
701 return fromFrame(frame()->tree()->nextSibling()); | |
702 } | |
703 | |
704 WebFrame* WebFrameImpl::previousSibling() const | |
705 { | |
706 if (!frame()) | |
707 return 0; | |
708 return fromFrame(frame()->tree()->previousSibling()); | |
709 } | |
710 | |
711 WebFrame* WebFrameImpl::traverseNext(bool wrap) const | |
712 { | |
713 if (!frame()) | |
714 return 0; | |
715 return fromFrame(frame()->tree()->traverseNextWithWrap(wrap)); | |
716 } | |
717 | |
718 WebFrame* WebFrameImpl::traversePrevious(bool wrap) const | |
719 { | |
720 if (!frame()) | |
721 return 0; | |
722 return fromFrame(frame()->tree()->traversePreviousWithWrap(wrap)); | |
723 } | |
724 | |
725 WebFrame* WebFrameImpl::findChildByName(const WebString& name) const | |
726 { | |
727 if (!frame()) | |
728 return 0; | |
729 return fromFrame(frame()->tree()->child(name)); | |
730 } | |
731 | |
732 WebFrame* WebFrameImpl::findChildByExpression(const WebString& xpath) const | |
733 { | |
734 if (xpath.isEmpty()) | |
735 return 0; | |
736 | |
737 Document* document = frame()->document(); | |
738 | |
739 ExceptionCode ec = 0; | |
740 RefPtr<XPathResult> xpathResult = document->evaluate(xpath, document, 0, XPa
thResult::ORDERED_NODE_ITERATOR_TYPE, 0, ec); | |
741 if (!xpathResult) | |
742 return 0; | |
743 | |
744 Node* node = xpathResult->iterateNext(ec); | |
745 if (!node || !node->isFrameOwnerElement()) | |
746 return 0; | |
747 HTMLFrameOwnerElement* frameElement = toFrameOwnerElement(node); | |
748 return fromFrame(frameElement->contentFrame()); | |
749 } | |
750 | |
751 WebDocument WebFrameImpl::document() const | |
752 { | |
753 if (!frame() || !frame()->document()) | |
754 return WebDocument(); | |
755 return WebDocument(frame()->document()); | |
756 } | |
757 | |
758 WebPerformance WebFrameImpl::performance() const | |
759 { | |
760 if (!frame()) | |
761 return WebPerformance(); | |
762 return WebPerformance(frame()->document()->domWindow()->performance()); | |
763 } | |
764 | |
765 NPObject* WebFrameImpl::windowObject() const | |
766 { | |
767 if (!frame()) | |
768 return 0; | |
769 return frame()->script()->windowScriptNPObject(); | |
770 } | |
771 | |
772 void WebFrameImpl::bindToWindowObject(const WebString& name, NPObject* object) | |
773 { | |
774 if (!frame() || !frame()->script()->canExecuteScripts(NotAboutToExecuteScrip
t)) | |
775 return; | |
776 frame()->script()->bindToWindowObject(frame(), String(name), object); | |
777 } | |
778 | |
779 void WebFrameImpl::executeScript(const WebScriptSource& source) | |
780 { | |
781 ASSERT(frame()); | |
782 TextPosition position(OrdinalNumber::fromOneBasedInt(source.startLine), Ordi
nalNumber::first()); | |
783 frame()->script()->executeScript(ScriptSourceCode(source.code, source.url, p
osition)); | |
784 } | |
785 | |
786 void WebFrameImpl::executeScriptInIsolatedWorld(int worldID, const WebScriptSour
ce* sourcesIn, unsigned numSources, int extensionGroup) | |
787 { | |
788 ASSERT(frame()); | |
789 ASSERT(worldID > 0); | |
790 | |
791 Vector<ScriptSourceCode> sources; | |
792 for (unsigned i = 0; i < numSources; ++i) { | |
793 TextPosition position(OrdinalNumber::fromOneBasedInt(sourcesIn[i].startL
ine), OrdinalNumber::first()); | |
794 sources.append(ScriptSourceCode(sourcesIn[i].code, sourcesIn[i].url, pos
ition)); | |
795 } | |
796 | |
797 frame()->script()->executeScriptInIsolatedWorld(worldID, sources, extensionG
roup, 0); | |
798 } | |
799 | |
800 void WebFrameImpl::setIsolatedWorldSecurityOrigin(int worldID, const WebSecurity
Origin& securityOrigin) | |
801 { | |
802 ASSERT(frame()); | |
803 DOMWrapperWorld::setIsolatedWorldSecurityOrigin(worldID, securityOrigin.get(
)); | |
804 } | |
805 | |
806 void WebFrameImpl::setIsolatedWorldContentSecurityPolicy(int worldID, const WebS
tring& policy) | |
807 { | |
808 ASSERT(frame()); | |
809 DOMWrapperWorld::setIsolatedWorldContentSecurityPolicy(worldID, policy); | |
810 } | |
811 | |
812 void WebFrameImpl::addMessageToConsole(const WebConsoleMessage& message) | |
813 { | |
814 ASSERT(frame()); | |
815 | |
816 MessageLevel webCoreMessageLevel; | |
817 switch (message.level) { | |
818 case WebConsoleMessage::LevelDebug: | |
819 webCoreMessageLevel = DebugMessageLevel; | |
820 break; | |
821 case WebConsoleMessage::LevelLog: | |
822 webCoreMessageLevel = LogMessageLevel; | |
823 break; | |
824 case WebConsoleMessage::LevelWarning: | |
825 webCoreMessageLevel = WarningMessageLevel; | |
826 break; | |
827 case WebConsoleMessage::LevelError: | |
828 webCoreMessageLevel = ErrorMessageLevel; | |
829 break; | |
830 default: | |
831 ASSERT_NOT_REACHED(); | |
832 return; | |
833 } | |
834 | |
835 frame()->document()->addConsoleMessage(OtherMessageSource, webCoreMessageLev
el, message.text); | |
836 } | |
837 | |
838 void WebFrameImpl::collectGarbage() | |
839 { | |
840 if (!frame()) | |
841 return; | |
842 if (!frame()->settings()->isScriptEnabled()) | |
843 return; | |
844 V8GCController::collectGarbage(v8::Isolate::GetCurrent()); | |
845 } | |
846 | |
847 bool WebFrameImpl::checkIfRunInsecureContent(const WebURL& url) const | |
848 { | |
849 ASSERT(frame()); | |
850 return frame()->loader()->mixedContentChecker()->canRunInsecureContent(frame
()->document()->securityOrigin(), url); | |
851 } | |
852 | |
853 v8::Handle<v8::Value> WebFrameImpl::executeScriptAndReturnValue(const WebScriptS
ource& source) | |
854 { | |
855 ASSERT(frame()); | |
856 | |
857 // FIXME: This fake user gesture is required to make a bunch of pyauto | |
858 // tests pass. If this isn't needed in non-test situations, we should | |
859 // consider removing this code and changing the tests. | |
860 // http://code.google.com/p/chromium/issues/detail?id=86397 | |
861 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture); | |
862 | |
863 TextPosition position(OrdinalNumber::fromOneBasedInt(source.startLine), Ordi
nalNumber::first()); | |
864 return frame()->script()->executeScript(ScriptSourceCode(source.code, source
.url, position)).v8Value(); | |
865 } | |
866 | |
867 void WebFrameImpl::executeScriptInIsolatedWorld(int worldID, const WebScriptSour
ce* sourcesIn, unsigned numSources, int extensionGroup, WebVector<v8::Local<v8::
Value> >* results) | |
868 { | |
869 ASSERT(frame()); | |
870 ASSERT(worldID > 0); | |
871 | |
872 Vector<ScriptSourceCode> sources; | |
873 | |
874 for (unsigned i = 0; i < numSources; ++i) { | |
875 TextPosition position(OrdinalNumber::fromOneBasedInt(sourcesIn[i].startL
ine), OrdinalNumber::first()); | |
876 sources.append(ScriptSourceCode(sourcesIn[i].code, sourcesIn[i].url, pos
ition)); | |
877 } | |
878 | |
879 if (results) { | |
880 Vector<ScriptValue> scriptResults; | |
881 frame()->script()->executeScriptInIsolatedWorld(worldID, sources, extens
ionGroup, &scriptResults); | |
882 WebVector<v8::Local<v8::Value> > v8Results(scriptResults.size()); | |
883 for (unsigned i = 0; i < scriptResults.size(); i++) | |
884 v8Results[i] = v8::Local<v8::Value>::New(scriptResults[i].v8Value())
; | |
885 results->swap(v8Results); | |
886 } else | |
887 frame()->script()->executeScriptInIsolatedWorld(worldID, sources, extens
ionGroup, 0); | |
888 } | |
889 | |
890 v8::Handle<v8::Value> WebFrameImpl::callFunctionEvenIfScriptDisabled(v8::Handle<
v8::Function> function, v8::Handle<v8::Object> receiver, int argc, v8::Handle<v8
::Value> argv[]) | |
891 { | |
892 ASSERT(frame()); | |
893 return frame()->script()->callFunctionEvenIfScriptDisabled(function, receive
r, argc, argv).v8Value(); | |
894 } | |
895 | |
896 v8::Local<v8::Context> WebFrameImpl::mainWorldScriptContext() const | |
897 { | |
898 if (!frame()) | |
899 return v8::Local<v8::Context>(); | |
900 return ScriptController::mainWorldContext(frame()); | |
901 } | |
902 | |
903 v8::Handle<v8::Value> WebFrameImpl::createFileSystem(WebFileSystemType type, con
st WebString& name, const WebString& path) | |
904 { | |
905 ASSERT(frame()); | |
906 return toV8(DOMFileSystem::create(frame()->document(), name, static_cast<Web
Core::FileSystemType>(type), KURL(ParsedURLString, path.utf8().data()), AsyncFil
eSystemChromium::create()), v8::Handle<v8::Object>(), frame()->script()->current
WorldContext()->GetIsolate()); | |
907 } | |
908 | |
909 v8::Handle<v8::Value> WebFrameImpl::createSerializableFileSystem(WebFileSystemTy
pe type, const WebString& name, const WebString& path) | |
910 { | |
911 ASSERT(frame()); | |
912 RefPtr<DOMFileSystem> fileSystem = DOMFileSystem::create(frame()->document()
, name, static_cast<WebCore::FileSystemType>(type), KURL(ParsedURLString, path.u
tf8().data()), AsyncFileSystemChromium::create()); | |
913 fileSystem->makeClonable(); | |
914 return toV8(fileSystem.release(), v8::Handle<v8::Object>(), frame()->script(
)->currentWorldContext()->GetIsolate()); | |
915 } | |
916 | |
917 v8::Handle<v8::Value> WebFrameImpl::createFileEntry(WebFileSystemType type, cons
t WebString& fileSystemName, const WebString& fileSystemPath, const WebString& f
ilePath, bool isDirectory) | |
918 { | |
919 ASSERT(frame()); | |
920 | |
921 RefPtr<DOMFileSystemBase> fileSystem = DOMFileSystem::create(frame()->docume
nt(), fileSystemName, static_cast<WebCore::FileSystemType>(type), KURL(ParsedURL
String, fileSystemPath.utf8().data()), AsyncFileSystemChromium::create()); | |
922 if (isDirectory) | |
923 return toV8(DirectoryEntry::create(fileSystem, filePath), v8::Handle<v8:
:Object>(), frame()->script()->currentWorldContext()->GetIsolate()); | |
924 return toV8(FileEntry::create(fileSystem, filePath), v8::Handle<v8::Object>(
), frame()->script()->currentWorldContext()->GetIsolate()); | |
925 } | |
926 | |
927 void WebFrameImpl::reload(bool ignoreCache) | |
928 { | |
929 ASSERT(frame()); | |
930 frame()->loader()->reload(ignoreCache); | |
931 } | |
932 | |
933 void WebFrameImpl::reloadWithOverrideURL(const WebURL& overrideUrl, bool ignoreC
ache) | |
934 { | |
935 ASSERT(frame()); | |
936 frame()->loader()->reload(ignoreCache, overrideUrl); | |
937 } | |
938 | |
939 void WebFrameImpl::loadRequest(const WebURLRequest& request) | |
940 { | |
941 ASSERT(frame()); | |
942 ASSERT(!request.isNull()); | |
943 const ResourceRequest& resourceRequest = request.toResourceRequest(); | |
944 | |
945 if (resourceRequest.url().protocolIs("javascript")) { | |
946 loadJavaScriptURL(resourceRequest.url()); | |
947 return; | |
948 } | |
949 | |
950 frame()->loader()->load(FrameLoadRequest(frame(), resourceRequest)); | |
951 } | |
952 | |
953 void WebFrameImpl::loadHistoryItem(const WebHistoryItem& item) | |
954 { | |
955 ASSERT(frame()); | |
956 RefPtr<HistoryItem> historyItem = PassRefPtr<HistoryItem>(item); | |
957 ASSERT(historyItem); | |
958 | |
959 frame()->loader()->prepareForHistoryNavigation(); | |
960 RefPtr<HistoryItem> currentItem = frame()->loader()->history()->currentItem(
); | |
961 m_inSameDocumentHistoryLoad = currentItem && currentItem->shouldDoSameDocume
ntNavigationTo(historyItem.get()); | |
962 frame()->page()->goToItem(historyItem.get()); | |
963 m_inSameDocumentHistoryLoad = false; | |
964 } | |
965 | |
966 void WebFrameImpl::loadData(const WebData& data, const WebString& mimeType, cons
t WebString& textEncoding, const WebURL& baseURL, const WebURL& unreachableURL,
bool replace) | |
967 { | |
968 ASSERT(frame()); | |
969 | |
970 // If we are loading substitute data to replace an existing load, then | |
971 // inherit all of the properties of that original request. This way, | |
972 // reload will re-attempt the original request. It is essential that | |
973 // we only do this when there is an unreachableURL since a non-empty | |
974 // unreachableURL informs FrameLoader::reload to load unreachableURL | |
975 // instead of the currently loaded URL. | |
976 ResourceRequest request; | |
977 if (replace && !unreachableURL.isEmpty()) | |
978 request = frame()->loader()->originalRequest(); | |
979 request.setURL(baseURL); | |
980 | |
981 FrameLoadRequest frameRequest(frame(), request, SubstituteData(data, mimeTyp
e, textEncoding, unreachableURL)); | |
982 ASSERT(frameRequest.substituteData().isValid()); | |
983 frame()->loader()->load(frameRequest); | |
984 if (replace) { | |
985 // Do this to force WebKit to treat the load as replacing the currently | |
986 // loaded page. | |
987 // FIXME: Can we use lock history instead? | |
988 frame()->loader()->setReplacing(); | |
989 } | |
990 } | |
991 | |
992 void WebFrameImpl::loadHTMLString(const WebData& data, const WebURL& baseURL, co
nst WebURL& unreachableURL, bool replace) | |
993 { | |
994 ASSERT(frame()); | |
995 loadData(data, WebString::fromUTF8("text/html"), WebString::fromUTF8("UTF-8"
), baseURL, unreachableURL, replace); | |
996 } | |
997 | |
998 bool WebFrameImpl::isLoading() const | |
999 { | |
1000 if (!frame()) | |
1001 return false; | |
1002 return frame()->loader()->isLoading(); | |
1003 } | |
1004 | |
1005 void WebFrameImpl::stopLoading() | |
1006 { | |
1007 if (!frame()) | |
1008 return; | |
1009 // FIXME: Figure out what we should really do here. It seems like a bug | |
1010 // that FrameLoader::stopLoading doesn't call stopAllLoaders. | |
1011 frame()->loader()->stopAllLoaders(); | |
1012 frame()->loader()->stopLoading(UnloadEventPolicyNone); | |
1013 } | |
1014 | |
1015 WebDataSource* WebFrameImpl::provisionalDataSource() const | |
1016 { | |
1017 ASSERT(frame()); | |
1018 | |
1019 // We regard the policy document loader as still provisional. | |
1020 DocumentLoader* documentLoader = frame()->loader()->provisionalDocumentLoade
r(); | |
1021 if (!documentLoader) | |
1022 documentLoader = frame()->loader()->policyDocumentLoader(); | |
1023 | |
1024 return DataSourceForDocLoader(documentLoader); | |
1025 } | |
1026 | |
1027 WebDataSource* WebFrameImpl::dataSource() const | |
1028 { | |
1029 ASSERT(frame()); | |
1030 return DataSourceForDocLoader(frame()->loader()->documentLoader()); | |
1031 } | |
1032 | |
1033 WebHistoryItem WebFrameImpl::previousHistoryItem() const | |
1034 { | |
1035 ASSERT(frame()); | |
1036 // We use the previous item here because documentState (filled-out forms) | |
1037 // only get saved to history when it becomes the previous item. The caller | |
1038 // is expected to query the history item after a navigation occurs, after | |
1039 // the desired history item has become the previous entry. | |
1040 return WebHistoryItem(frame()->loader()->history()->previousItem()); | |
1041 } | |
1042 | |
1043 WebHistoryItem WebFrameImpl::currentHistoryItem() const | |
1044 { | |
1045 ASSERT(frame()); | |
1046 | |
1047 // We're shutting down. | |
1048 if (!frame()->loader()->activeDocumentLoader()) | |
1049 return WebHistoryItem(); | |
1050 | |
1051 // If we are still loading, then we don't want to clobber the current | |
1052 // history item as this could cause us to lose the scroll position and | |
1053 // document state. However, it is OK for new navigations. | |
1054 // FIXME: Can we make this a plain old getter, instead of worrying about | |
1055 // clobbering here? | |
1056 if (!m_inSameDocumentHistoryLoad && (frame()->loader()->loadType() == FrameL
oadTypeStandard | |
1057 || !frame()->loader()->activeDocumentLoader()->isLoadingInAPISense())) | |
1058 frame()->loader()->history()->saveDocumentAndScrollState(); | |
1059 | |
1060 return WebHistoryItem(frame()->page()->backForward()->currentItem()); | |
1061 } | |
1062 | |
1063 void WebFrameImpl::enableViewSourceMode(bool enable) | |
1064 { | |
1065 if (frame()) | |
1066 frame()->setInViewSourceMode(enable); | |
1067 } | |
1068 | |
1069 bool WebFrameImpl::isViewSourceModeEnabled() const | |
1070 { | |
1071 if (!frame()) | |
1072 return false; | |
1073 return frame()->inViewSourceMode(); | |
1074 } | |
1075 | |
1076 void WebFrameImpl::setReferrerForRequest(WebURLRequest& request, const WebURL& r
eferrerURL) | |
1077 { | |
1078 String referrer = referrerURL.isEmpty() ? frame()->loader()->outgoingReferre
r() : String(referrerURL.spec().utf16()); | |
1079 referrer = SecurityPolicy::generateReferrerHeader(frame()->document()->refer
rerPolicy(), request.url(), referrer); | |
1080 if (referrer.isEmpty()) | |
1081 return; | |
1082 request.setHTTPHeaderField(WebString::fromUTF8("Referer"), referrer); | |
1083 } | |
1084 | |
1085 void WebFrameImpl::dispatchWillSendRequest(WebURLRequest& request) | |
1086 { | |
1087 ResourceResponse response; | |
1088 frame()->loader()->client()->dispatchWillSendRequest(0, 0, request.toMutable
ResourceRequest(), response); | |
1089 } | |
1090 | |
1091 WebURLLoader* WebFrameImpl::createAssociatedURLLoader(const WebURLLoaderOptions&
options) | |
1092 { | |
1093 return new AssociatedURLLoader(this, options); | |
1094 } | |
1095 | |
1096 unsigned WebFrameImpl::unloadListenerCount() const | |
1097 { | |
1098 return frame()->document()->domWindow()->pendingUnloadEventListeners(); | |
1099 } | |
1100 | |
1101 bool WebFrameImpl::willSuppressOpenerInNewFrame() const | |
1102 { | |
1103 return frame()->loader()->suppressOpenerInNewFrame(); | |
1104 } | |
1105 | |
1106 void WebFrameImpl::replaceSelection(const WebString& text) | |
1107 { | |
1108 bool selectReplacement = false; | |
1109 bool smartReplace = true; | |
1110 frame()->editor()->replaceSelectionWithText(text, selectReplacement, smartRe
place); | |
1111 } | |
1112 | |
1113 void WebFrameImpl::insertText(const WebString& text) | |
1114 { | |
1115 if (frame()->editor()->hasComposition()) | |
1116 frame()->editor()->confirmComposition(text); | |
1117 else | |
1118 frame()->editor()->insertText(text, 0); | |
1119 } | |
1120 | |
1121 void WebFrameImpl::setMarkedText(const WebString& text, unsigned location, unsig
ned length) | |
1122 { | |
1123 Vector<CompositionUnderline> decorations; | |
1124 frame()->editor()->setComposition(text, decorations, location, length); | |
1125 } | |
1126 | |
1127 void WebFrameImpl::unmarkText() | |
1128 { | |
1129 frame()->editor()->cancelComposition(); | |
1130 } | |
1131 | |
1132 bool WebFrameImpl::hasMarkedText() const | |
1133 { | |
1134 return frame()->editor()->hasComposition(); | |
1135 } | |
1136 | |
1137 WebRange WebFrameImpl::markedRange() const | |
1138 { | |
1139 return frame()->editor()->compositionRange(); | |
1140 } | |
1141 | |
1142 bool WebFrameImpl::firstRectForCharacterRange(unsigned location, unsigned length
, WebRect& rect) const | |
1143 { | |
1144 if ((location + length < location) && (location + length)) | |
1145 length = 0; | |
1146 | |
1147 RefPtr<Range> range = TextIterator::rangeFromLocationAndLength(frame()->sele
ction()->rootEditableElementOrDocumentElement(), location, length); | |
1148 if (!range) | |
1149 return false; | |
1150 IntRect intRect = frame()->editor()->firstRectForRange(range.get()); | |
1151 rect = WebRect(intRect); | |
1152 rect = frame()->view()->contentsToWindow(rect); | |
1153 return true; | |
1154 } | |
1155 | |
1156 size_t WebFrameImpl::characterIndexForPoint(const WebPoint& webPoint) const | |
1157 { | |
1158 if (!frame()) | |
1159 return notFound; | |
1160 | |
1161 IntPoint point = frame()->view()->windowToContents(webPoint); | |
1162 HitTestResult result = frame()->eventHandler()->hitTestResultAtPoint(point,
HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShad
owContent); | |
1163 RefPtr<Range> range = frame()->rangeForPoint(result.roundedPointInInnerNodeF
rame()); | |
1164 if (!range) | |
1165 return notFound; | |
1166 | |
1167 size_t location, length; | |
1168 TextIterator::getLocationAndLengthFromRange(frame()->selection()->rootEditab
leElementOrDocumentElement(), range.get(), location, length); | |
1169 return location; | |
1170 } | |
1171 | |
1172 bool WebFrameImpl::executeCommand(const WebString& name, const WebNode& node) | |
1173 { | |
1174 ASSERT(frame()); | |
1175 | |
1176 if (name.length() <= 2) | |
1177 return false; | |
1178 | |
1179 // Since we don't have NSControl, we will convert the format of command | |
1180 // string and call the function on Editor directly. | |
1181 String command = name; | |
1182 | |
1183 // Make sure the first letter is upper case. | |
1184 command.replace(0, 1, command.substring(0, 1).upper()); | |
1185 | |
1186 // Remove the trailing ':' if existing. | |
1187 if (command[command.length() - 1] == UChar(':')) | |
1188 command = command.substring(0, command.length() - 1); | |
1189 | |
1190 WebPluginContainerImpl* pluginContainer = pluginContainerFromNode(frame(), n
ode); | |
1191 if (pluginContainer && pluginContainer->executeEditCommand(name)) | |
1192 return true; | |
1193 | |
1194 bool result = true; | |
1195 | |
1196 // Specially handling commands that Editor::execCommand does not directly | |
1197 // support. | |
1198 if (command == "DeleteToEndOfParagraph") { | |
1199 if (!frame()->editor()->deleteWithDirection(DirectionForward, ParagraphB
oundary, true, false)) | |
1200 frame()->editor()->deleteWithDirection(DirectionForward, CharacterGr
anularity, true, false); | |
1201 } else if (command == "Indent") | |
1202 frame()->editor()->indent(); | |
1203 else if (command == "Outdent") | |
1204 frame()->editor()->outdent(); | |
1205 else if (command == "DeleteBackward") | |
1206 result = frame()->editor()->command(AtomicString("BackwardDelete")).exec
ute(); | |
1207 else if (command == "DeleteForward") | |
1208 result = frame()->editor()->command(AtomicString("ForwardDelete")).execu
te(); | |
1209 else if (command == "AdvanceToNextMisspelling") { | |
1210 // Wee need to pass false here or else the currently selected word will
never be skipped. | |
1211 frame()->editor()->advanceToNextMisspelling(false); | |
1212 } else if (command == "ToggleSpellPanel") | |
1213 frame()->editor()->showSpellingGuessPanel(); | |
1214 else | |
1215 result = frame()->editor()->command(command).execute(); | |
1216 return result; | |
1217 } | |
1218 | |
1219 bool WebFrameImpl::executeCommand(const WebString& name, const WebString& value,
const WebNode& node) | |
1220 { | |
1221 ASSERT(frame()); | |
1222 String webName = name; | |
1223 | |
1224 WebPluginContainerImpl* pluginContainer = pluginContainerFromNode(frame(), n
ode); | |
1225 if (pluginContainer && pluginContainer->executeEditCommand(name, value)) | |
1226 return true; | |
1227 | |
1228 // moveToBeginningOfDocument and moveToEndfDocument are only handled by WebK
it for editable nodes. | |
1229 if (!frame()->editor()->canEdit() && webName == "moveToBeginningOfDocument") | |
1230 return viewImpl()->propagateScroll(ScrollUp, ScrollByDocument); | |
1231 | |
1232 if (!frame()->editor()->canEdit() && webName == "moveToEndOfDocument") | |
1233 return viewImpl()->propagateScroll(ScrollDown, ScrollByDocument); | |
1234 | |
1235 return frame()->editor()->command(webName).execute(value); | |
1236 } | |
1237 | |
1238 bool WebFrameImpl::isCommandEnabled(const WebString& name) const | |
1239 { | |
1240 ASSERT(frame()); | |
1241 return frame()->editor()->command(name).isEnabled(); | |
1242 } | |
1243 | |
1244 void WebFrameImpl::enableContinuousSpellChecking(bool enable) | |
1245 { | |
1246 if (enable == isContinuousSpellCheckingEnabled()) | |
1247 return; | |
1248 frame()->editor()->toggleContinuousSpellChecking(); | |
1249 } | |
1250 | |
1251 bool WebFrameImpl::isContinuousSpellCheckingEnabled() const | |
1252 { | |
1253 return frame()->editor()->isContinuousSpellCheckingEnabled(); | |
1254 } | |
1255 | |
1256 void WebFrameImpl::requestTextChecking(const WebElement& webElement) | |
1257 { | |
1258 if (webElement.isNull()) | |
1259 return; | |
1260 RefPtr<Range> rangeToCheck = rangeOfContents(const_cast<Element*>(webElement
.constUnwrap<Element>())); | |
1261 frame()->editor()->spellChecker()->requestCheckingFor(SpellCheckRequest::cre
ate(TextCheckingTypeSpelling | TextCheckingTypeGrammar, TextCheckingProcessBatch
, rangeToCheck, rangeToCheck)); | |
1262 } | |
1263 | |
1264 void WebFrameImpl::replaceMisspelledRange(const WebString& text) | |
1265 { | |
1266 // If this caret selection has two or more markers, this function replace th
e range covered by the first marker with the specified word as Microsoft Word do
es. | |
1267 if (pluginContainerFromFrame(frame())) | |
1268 return; | |
1269 RefPtr<Range> caretRange = frame()->selection()->toNormalizedRange(); | |
1270 if (!caretRange) | |
1271 return; | |
1272 Vector<DocumentMarker*> markers = frame()->document()->markers()->markersInR
ange(caretRange.get(), DocumentMarker::Spelling | DocumentMarker::Grammar); | |
1273 if (markers.size() < 1 || markers[0]->startOffset() >= markers[0]->endOffset
()) | |
1274 return; | |
1275 RefPtr<Range> markerRange = Range::create(caretRange->ownerDocument(), caret
Range->startContainer(), markers[0]->startOffset(), caretRange->endContainer(),
markers[0]->endOffset()); | |
1276 if (!markerRange) | |
1277 return; | |
1278 if (!frame()->selection()->shouldChangeSelection(markerRange.get())) | |
1279 return; | |
1280 frame()->selection()->setSelection(markerRange.get(), CharacterGranularity); | |
1281 frame()->editor()->replaceSelectionWithText(text, false, false); | |
1282 } | |
1283 | |
1284 void WebFrameImpl::removeSpellingMarkers() | |
1285 { | |
1286 frame()->document()->markers()->removeMarkers(DocumentMarker::Spelling | Doc
umentMarker::Grammar); | |
1287 } | |
1288 | |
1289 bool WebFrameImpl::hasSelection() const | |
1290 { | |
1291 WebPluginContainerImpl* pluginContainer = pluginContainerFromFrame(frame()); | |
1292 if (pluginContainer) | |
1293 return pluginContainer->plugin()->hasSelection(); | |
1294 | |
1295 // frame()->selection()->isNone() never returns true. | |
1296 return (frame()->selection()->start() != frame()->selection()->end()); | |
1297 } | |
1298 | |
1299 WebRange WebFrameImpl::selectionRange() const | |
1300 { | |
1301 return frame()->selection()->toNormalizedRange(); | |
1302 } | |
1303 | |
1304 WebString WebFrameImpl::selectionAsText() const | |
1305 { | |
1306 WebPluginContainerImpl* pluginContainer = pluginContainerFromFrame(frame()); | |
1307 if (pluginContainer) | |
1308 return pluginContainer->plugin()->selectionAsText(); | |
1309 | |
1310 RefPtr<Range> range = frame()->selection()->toNormalizedRange(); | |
1311 if (!range) | |
1312 return WebString(); | |
1313 | |
1314 String text = range->text(); | |
1315 #if OS(WINDOWS) | |
1316 replaceNewlinesWithWindowsStyleNewlines(text); | |
1317 #endif | |
1318 replaceNBSPWithSpace(text); | |
1319 return text; | |
1320 } | |
1321 | |
1322 WebString WebFrameImpl::selectionAsMarkup() const | |
1323 { | |
1324 WebPluginContainerImpl* pluginContainer = pluginContainerFromFrame(frame()); | |
1325 if (pluginContainer) | |
1326 return pluginContainer->plugin()->selectionAsMarkup(); | |
1327 | |
1328 RefPtr<Range> range = frame()->selection()->toNormalizedRange(); | |
1329 if (!range) | |
1330 return WebString(); | |
1331 | |
1332 return createMarkup(range.get(), 0, AnnotateForInterchange, false, ResolveNo
nLocalURLs); | |
1333 } | |
1334 | |
1335 void WebFrameImpl::selectWordAroundPosition(Frame* frame, VisiblePosition positi
on) | |
1336 { | |
1337 VisibleSelection selection(position); | |
1338 selection.expandUsingGranularity(WordGranularity); | |
1339 | |
1340 if (frame->selection()->shouldChangeSelection(selection)) { | |
1341 TextGranularity granularity = selection.isRange() ? WordGranularity : Ch
aracterGranularity; | |
1342 frame->selection()->setSelection(selection, granularity); | |
1343 } | |
1344 } | |
1345 | |
1346 bool WebFrameImpl::selectWordAroundCaret() | |
1347 { | |
1348 FrameSelection* selection = frame()->selection(); | |
1349 ASSERT(!selection->isNone()); | |
1350 if (selection->isNone() || selection->isRange()) | |
1351 return false; | |
1352 selectWordAroundPosition(frame(), selection->selection().visibleStart()); | |
1353 return true; | |
1354 } | |
1355 | |
1356 void WebFrameImpl::selectRange(const WebPoint& base, const WebPoint& extent) | |
1357 { | |
1358 moveRangeSelection(base, extent); | |
1359 } | |
1360 | |
1361 void WebFrameImpl::selectRange(const WebRange& webRange) | |
1362 { | |
1363 if (RefPtr<Range> range = static_cast<PassRefPtr<Range> >(webRange)) | |
1364 frame()->selection()->setSelectedRange(range.get(), WebCore::VP_DEFAULT_
AFFINITY, false); | |
1365 } | |
1366 | |
1367 void WebFrameImpl::moveCaretSelectionTowardsWindowPoint(const WebPoint& point) | |
1368 { | |
1369 moveCaretSelection(point); | |
1370 } | |
1371 | |
1372 void WebFrameImpl::moveRangeSelection(const WebPoint& base, const WebPoint& exte
nt) | |
1373 { | |
1374 FrameSelection* selection = frame()->selection(); | |
1375 if (!selection) | |
1376 return; | |
1377 | |
1378 VisiblePosition basePosition = visiblePositionForWindowPoint(base); | |
1379 VisiblePosition extentPosition = visiblePositionForWindowPoint(extent); | |
1380 VisibleSelection newSelection = VisibleSelection(basePosition, extentPositio
n); | |
1381 if (frame()->selection()->shouldChangeSelection(newSelection)) | |
1382 frame()->selection()->setSelection(newSelection, CharacterGranularity); | |
1383 } | |
1384 | |
1385 void WebFrameImpl::moveCaretSelection(const WebPoint& point) | |
1386 { | |
1387 Element* editable = frame()->selection()->rootEditableElement(); | |
1388 if (!editable) | |
1389 return; | |
1390 | |
1391 VisiblePosition position = visiblePositionForWindowPoint(point); | |
1392 if (frame()->selection()->shouldChangeSelection(position)) | |
1393 frame()->selection()->moveTo(position, UserTriggered); | |
1394 } | |
1395 | |
1396 VisiblePosition WebFrameImpl::visiblePositionForWindowPoint(const WebPoint& poin
t) | |
1397 { | |
1398 FloatPoint unscaledPoint(point); | |
1399 unscaledPoint.scale(1 / view()->pageScaleFactor(), 1 / view()->pageScaleFact
or()); | |
1400 | |
1401 HitTestRequest request = HitTestRequest::Move | HitTestRequest::ReadOnly | H
itTestRequest::Active | HitTestRequest::IgnoreClipping | HitTestRequest::Disallo
wShadowContent; | |
1402 HitTestResult result(frame()->view()->windowToContents(roundedIntPoint(unsca
ledPoint))); | |
1403 frame()->document()->renderView()->layer()->hitTest(request, result); | |
1404 | |
1405 Node* node = result.targetNode(); | |
1406 if (!node) | |
1407 return VisiblePosition(); | |
1408 return frame()->selection()->selection().visiblePositionRespectingEditingBou
ndary(result.localPoint(), node); | |
1409 } | |
1410 | |
1411 int WebFrameImpl::printBegin(const WebPrintParams& printParams, const WebNode& c
onstrainToNode, bool* useBrowserOverlays) | |
1412 { | |
1413 ASSERT(!frame()->document()->isFrameSet()); | |
1414 WebPluginContainerImpl* pluginContainer = 0; | |
1415 if (constrainToNode.isNull()) { | |
1416 // If this is a plugin document, check if the plugin supports its own | |
1417 // printing. If it does, we will delegate all printing to that. | |
1418 pluginContainer = pluginContainerFromFrame(frame()); | |
1419 } else { | |
1420 // We only support printing plugin nodes for now. | |
1421 pluginContainer = static_cast<WebPluginContainerImpl*>(constrainToNode.p
luginContainer()); | |
1422 } | |
1423 | |
1424 if (pluginContainer && pluginContainer->supportsPaginatedPrint()) | |
1425 m_printContext = adoptPtr(new ChromePluginPrintContext(frame(), pluginCo
ntainer, printParams)); | |
1426 else | |
1427 m_printContext = adoptPtr(new ChromePrintContext(frame())); | |
1428 | |
1429 FloatRect rect(0, 0, static_cast<float>(printParams.printContentArea.width),
static_cast<float>(printParams.printContentArea.height)); | |
1430 m_printContext->begin(rect.width(), rect.height()); | |
1431 float pageHeight; | |
1432 // We ignore the overlays calculation for now since they are generated in th
e | |
1433 // browser. pageHeight is actually an output parameter. | |
1434 m_printContext->computePageRects(rect, 0, 0, 1.0, pageHeight); | |
1435 if (useBrowserOverlays) | |
1436 *useBrowserOverlays = m_printContext->shouldUseBrowserOverlays(); | |
1437 | |
1438 return m_printContext->pageCount(); | |
1439 } | |
1440 | |
1441 float WebFrameImpl::getPrintPageShrink(int page) | |
1442 { | |
1443 ASSERT(m_printContext && page >= 0); | |
1444 return m_printContext->getPageShrink(page); | |
1445 } | |
1446 | |
1447 float WebFrameImpl::printPage(int page, WebCanvas* canvas) | |
1448 { | |
1449 #if ENABLE(PRINTING) | |
1450 ASSERT(m_printContext && page >= 0 && frame() && frame()->document()); | |
1451 | |
1452 GraphicsContext graphicsContext(canvas); | |
1453 graphicsContext.setPrinting(true); | |
1454 return m_printContext->spoolPage(graphicsContext, page); | |
1455 #else | |
1456 return 0; | |
1457 #endif | |
1458 } | |
1459 | |
1460 void WebFrameImpl::printEnd() | |
1461 { | |
1462 ASSERT(m_printContext); | |
1463 m_printContext->end(); | |
1464 m_printContext.clear(); | |
1465 } | |
1466 | |
1467 bool WebFrameImpl::isPrintScalingDisabledForPlugin(const WebNode& node) | |
1468 { | |
1469 WebPluginContainerImpl* pluginContainer = node.isNull() ? pluginContainerFr
omFrame(frame()) : static_cast<WebPluginContainerImpl*>(node.pluginContainer()); | |
1470 | |
1471 if (!pluginContainer || !pluginContainer->supportsPaginatedPrint()) | |
1472 return false; | |
1473 | |
1474 return pluginContainer->isPrintScalingDisabled(); | |
1475 } | |
1476 | |
1477 bool WebFrameImpl::hasCustomPageSizeStyle(int pageIndex) | |
1478 { | |
1479 return frame()->document()->styleForPage(pageIndex)->pageSizeType() != PAGE_
SIZE_AUTO; | |
1480 } | |
1481 | |
1482 bool WebFrameImpl::isPageBoxVisible(int pageIndex) | |
1483 { | |
1484 return frame()->document()->isPageBoxVisible(pageIndex); | |
1485 } | |
1486 | |
1487 void WebFrameImpl::pageSizeAndMarginsInPixels(int pageIndex, WebSize& pageSize,
int& marginTop, int& marginRight, int& marginBottom, int& marginLeft) | |
1488 { | |
1489 IntSize size = pageSize; | |
1490 frame()->document()->pageSizeAndMarginsInPixels(pageIndex, size, marginTop,
marginRight, marginBottom, marginLeft); | |
1491 pageSize = size; | |
1492 } | |
1493 | |
1494 WebString WebFrameImpl::pageProperty(const WebString& propertyName, int pageInde
x) | |
1495 { | |
1496 ASSERT(m_printContext); | |
1497 return m_printContext->pageProperty(frame(), propertyName.utf8().data(), pag
eIndex); | |
1498 } | |
1499 | |
1500 bool WebFrameImpl::find(int identifier, const WebString& searchText, const WebFi
ndOptions& options, bool wrapWithinFrame, WebRect* selectionRect) | |
1501 { | |
1502 if (!frame() || !frame()->page()) | |
1503 return false; | |
1504 | |
1505 WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl(); | |
1506 | |
1507 if (!options.findNext) | |
1508 frame()->page()->unmarkAllTextMatches(); | |
1509 else | |
1510 setMarkerActive(m_activeMatch.get(), false); | |
1511 | |
1512 if (m_activeMatch && m_activeMatch->ownerDocument() != frame()->document()) | |
1513 m_activeMatch = 0; | |
1514 | |
1515 // If the user has selected something since the last Find operation we want | |
1516 // to start from there. Otherwise, we start searching from where the last Fi
nd | |
1517 // operation left off (either a Find or a FindNext operation). | |
1518 VisibleSelection selection(frame()->selection()->selection()); | |
1519 bool activeSelection = !selection.isNone(); | |
1520 if (activeSelection) { | |
1521 m_activeMatch = selection.firstRange().get(); | |
1522 frame()->selection()->clear(); | |
1523 } | |
1524 | |
1525 ASSERT(frame() && frame()->view()); | |
1526 const FindOptions findOptions = (options.forward ? 0 : Backwards) | |
1527 | (options.matchCase ? 0 : CaseInsensitive) | |
1528 | (wrapWithinFrame ? WrapAround : 0) | |
1529 | (!options.findNext ? StartInSelection : 0); | |
1530 m_activeMatch = frame()->editor()->findStringAndScrollToVisible(searchText,
m_activeMatch.get(), findOptions); | |
1531 | |
1532 if (!m_activeMatch) { | |
1533 // If we're finding next the next active match might not be in the curre
nt frame. | |
1534 // In this case we don't want to clear the matches cache. | |
1535 if (!options.findNext) | |
1536 clearFindMatchesCache(); | |
1537 invalidateArea(InvalidateAll); | |
1538 return false; | |
1539 } | |
1540 | |
1541 #if OS(ANDROID) | |
1542 viewImpl()->zoomToFindInPageRect(frameView()->contentsToWindow(enclosingIntR
ect(RenderObject::absoluteBoundingBoxRectForRange(m_activeMatch.get())))); | |
1543 #endif | |
1544 | |
1545 setMarkerActive(m_activeMatch.get(), true); | |
1546 WebFrameImpl* oldActiveFrame = mainFrameImpl->m_currentActiveMatchFrame; | |
1547 mainFrameImpl->m_currentActiveMatchFrame = this; | |
1548 | |
1549 // Make sure no node is focused. See http://crbug.com/38700. | |
1550 frame()->document()->setFocusedNode(0); | |
1551 | |
1552 if (!options.findNext || activeSelection) { | |
1553 // This is either a Find operation or a Find-next from a new start point | |
1554 // due to a selection, so we set the flag to ask the scoping effort | |
1555 // to find the active rect for us and report it back to the UI. | |
1556 m_locatingActiveRect = true; | |
1557 } else { | |
1558 if (oldActiveFrame != this) { | |
1559 if (options.forward) | |
1560 m_activeMatchIndexInCurrentFrame = 0; | |
1561 else | |
1562 m_activeMatchIndexInCurrentFrame = m_lastMatchCount - 1; | |
1563 } else { | |
1564 if (options.forward) | |
1565 ++m_activeMatchIndexInCurrentFrame; | |
1566 else | |
1567 --m_activeMatchIndexInCurrentFrame; | |
1568 | |
1569 if (m_activeMatchIndexInCurrentFrame + 1 > m_lastMatchCount) | |
1570 m_activeMatchIndexInCurrentFrame = 0; | |
1571 if (m_activeMatchIndexInCurrentFrame == -1) | |
1572 m_activeMatchIndexInCurrentFrame = m_lastMatchCount - 1; | |
1573 } | |
1574 if (selectionRect) { | |
1575 *selectionRect = frameView()->contentsToWindow(m_activeMatch->boundi
ngBox()); | |
1576 reportFindInPageSelection(*selectionRect, m_activeMatchIndexInCurren
tFrame + 1, identifier); | |
1577 } | |
1578 } | |
1579 | |
1580 return true; | |
1581 } | |
1582 | |
1583 void WebFrameImpl::stopFinding(bool clearSelection) | |
1584 { | |
1585 if (!clearSelection) | |
1586 setFindEndstateFocusAndSelection(); | |
1587 cancelPendingScopingEffort(); | |
1588 | |
1589 // Remove all markers for matches found and turn off the highlighting. | |
1590 frame()->document()->markers()->removeMarkers(DocumentMarker::TextMatch); | |
1591 frame()->editor()->setMarkedTextMatchesAreHighlighted(false); | |
1592 clearFindMatchesCache(); | |
1593 | |
1594 // Let the frame know that we don't want tickmarks or highlighting anymore. | |
1595 invalidateArea(InvalidateAll); | |
1596 } | |
1597 | |
1598 void WebFrameImpl::scopeStringMatches(int identifier, const WebString& searchTex
t, const WebFindOptions& options, bool reset) | |
1599 { | |
1600 if (reset) { | |
1601 // This is a brand new search, so we need to reset everything. | |
1602 // Scoping is just about to begin. | |
1603 m_scopingInProgress = true; | |
1604 | |
1605 // Need to keep the current identifier locally in order to finish the | |
1606 // request in case the frame is detached during the process. | |
1607 m_findRequestIdentifier = identifier; | |
1608 | |
1609 // Clear highlighting for this frame. | |
1610 if (frame() && frame()->page() && frame()->editor()->markedTextMatchesAr
eHighlighted()) | |
1611 frame()->page()->unmarkAllTextMatches(); | |
1612 | |
1613 // Clear the tickmarks and results cache. | |
1614 clearFindMatchesCache(); | |
1615 | |
1616 // Clear the counters from last operation. | |
1617 m_lastMatchCount = 0; | |
1618 m_nextInvalidateAfter = 0; | |
1619 | |
1620 m_resumeScopingFromRange = 0; | |
1621 | |
1622 // The view might be null on detached frames. | |
1623 if (frame() && frame()->page()) | |
1624 viewImpl()->mainFrameImpl()->m_framesScopingCount++; | |
1625 | |
1626 // Now, defer scoping until later to allow find operation to finish quic
kly. | |
1627 scopeStringMatchesSoon(identifier, searchText, options, false); // false
means just reset, so don't do it again. | |
1628 return; | |
1629 } | |
1630 | |
1631 if (!shouldScopeMatches(searchText)) { | |
1632 // Note that we want to defer the final update when resetting even if sh
ouldScopeMatches returns false. | |
1633 // This is done in order to prevent sending a final message based only o
n the results of the first frame | |
1634 // since m_framesScopingCount would be 0 as other frames have yet to res
et. | |
1635 finishCurrentScopingEffort(identifier); | |
1636 return; | |
1637 } | |
1638 | |
1639 WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl(); | |
1640 RefPtr<Range> searchRange(rangeOfContents(frame()->document())); | |
1641 | |
1642 Node* originalEndContainer = searchRange->endContainer(); | |
1643 int originalEndOffset = searchRange->endOffset(); | |
1644 | |
1645 ExceptionCode ec = 0, ec2 = 0; | |
1646 if (m_resumeScopingFromRange) { | |
1647 // This is a continuation of a scoping operation that timed out and didn
't | |
1648 // complete last time around, so we should start from where we left off. | |
1649 searchRange->setStart(m_resumeScopingFromRange->startContainer(), | |
1650 m_resumeScopingFromRange->startOffset(ec2) + 1, | |
1651 ec); | |
1652 if (ec || ec2) { | |
1653 if (ec2) // A non-zero |ec| happens when navigating during search. | |
1654 ASSERT_NOT_REACHED(); | |
1655 return; | |
1656 } | |
1657 } | |
1658 | |
1659 // This timeout controls how long we scope before releasing control. This | |
1660 // value does not prevent us from running for longer than this, but it is | |
1661 // periodically checked to see if we have exceeded our allocated time. | |
1662 const double maxScopingDuration = 0.1; // seconds | |
1663 | |
1664 int matchCount = 0; | |
1665 bool timedOut = false; | |
1666 double startTime = currentTime(); | |
1667 do { | |
1668 // Find next occurrence of the search string. | |
1669 // FIXME: (http://b/1088245) This WebKit operation may run for longer | |
1670 // than the timeout value, and is not interruptible as it is currently | |
1671 // written. We may need to rewrite it with interruptibility in mind, or | |
1672 // find an alternative. | |
1673 RefPtr<Range> resultRange(findPlainText(searchRange.get(), | |
1674 searchText, | |
1675 options.matchCase ? 0 : CaseInse
nsitive)); | |
1676 if (resultRange->collapsed(ec)) { | |
1677 if (!resultRange->startContainer()->isInShadowTree()) | |
1678 break; | |
1679 | |
1680 searchRange->setStartAfter( | |
1681 resultRange->startContainer()->deprecatedShadowAncestorNode(), e
c); | |
1682 searchRange->setEnd(originalEndContainer, originalEndOffset, ec); | |
1683 continue; | |
1684 } | |
1685 | |
1686 ++matchCount; | |
1687 | |
1688 // Catch a special case where Find found something but doesn't know what | |
1689 // the bounding box for it is. In this case we set the first match we fi
nd | |
1690 // as the active rect. | |
1691 IntRect resultBounds = resultRange->boundingBox(); | |
1692 IntRect activeSelectionRect; | |
1693 if (m_locatingActiveRect) { | |
1694 activeSelectionRect = m_activeMatch.get() ? | |
1695 m_activeMatch->boundingBox() : resultBounds; | |
1696 } | |
1697 | |
1698 // If the Find function found a match it will have stored where the | |
1699 // match was found in m_activeSelectionRect on the current frame. If we | |
1700 // find this rect during scoping it means we have found the active | |
1701 // tickmark. | |
1702 bool foundActiveMatch = false; | |
1703 if (m_locatingActiveRect && (activeSelectionRect == resultBounds)) { | |
1704 // We have found the active tickmark frame. | |
1705 mainFrameImpl->m_currentActiveMatchFrame = this; | |
1706 foundActiveMatch = true; | |
1707 // We also know which tickmark is active now. | |
1708 m_activeMatchIndexInCurrentFrame = matchCount - 1; | |
1709 // To stop looking for the active tickmark, we set this flag. | |
1710 m_locatingActiveRect = false; | |
1711 | |
1712 // Notify browser of new location for the selected rectangle. | |
1713 reportFindInPageSelection( | |
1714 frameView()->contentsToWindow(resultBounds), | |
1715 m_activeMatchIndexInCurrentFrame + 1, | |
1716 identifier); | |
1717 } | |
1718 | |
1719 addMarker(resultRange.get(), foundActiveMatch); | |
1720 | |
1721 m_findMatchesCache.append(FindMatch(resultRange.get(), m_lastMatchCount
+ matchCount)); | |
1722 | |
1723 // Set the new start for the search range to be the end of the previous | |
1724 // result range. There is no need to use a VisiblePosition here, | |
1725 // since findPlainText will use a TextIterator to go over the visible | |
1726 // text nodes. | |
1727 searchRange->setStart(resultRange->endContainer(ec), resultRange->endOff
set(ec), ec); | |
1728 | |
1729 Node* shadowTreeRoot = searchRange->shadowRoot(); | |
1730 if (searchRange->collapsed(ec) && shadowTreeRoot) | |
1731 searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount()
, ec); | |
1732 | |
1733 m_resumeScopingFromRange = resultRange; | |
1734 timedOut = (currentTime() - startTime) >= maxScopingDuration; | |
1735 } while (!timedOut); | |
1736 | |
1737 // Remember what we search for last time, so we can skip searching if more | |
1738 // letters are added to the search string (and last outcome was 0). | |
1739 m_lastSearchString = searchText; | |
1740 | |
1741 if (matchCount > 0) { | |
1742 frame()->editor()->setMarkedTextMatchesAreHighlighted(true); | |
1743 | |
1744 m_lastMatchCount += matchCount; | |
1745 | |
1746 // Let the mainframe know how much we found during this pass. | |
1747 mainFrameImpl->increaseMatchCount(matchCount, identifier); | |
1748 } | |
1749 | |
1750 if (timedOut) { | |
1751 // If we found anything during this pass, we should redraw. However, we | |
1752 // don't want to spam too much if the page is extremely long, so if we | |
1753 // reach a certain point we start throttling the redraw requests. | |
1754 if (matchCount > 0) | |
1755 invalidateIfNecessary(); | |
1756 | |
1757 // Scoping effort ran out of time, lets ask for another time-slice. | |
1758 scopeStringMatchesSoon( | |
1759 identifier, | |
1760 searchText, | |
1761 options, | |
1762 false); // don't reset. | |
1763 return; // Done for now, resume work later. | |
1764 } | |
1765 | |
1766 finishCurrentScopingEffort(identifier); | |
1767 } | |
1768 | |
1769 void WebFrameImpl::flushCurrentScopingEffort(int identifier) | |
1770 { | |
1771 if (!frame() || !frame()->page()) | |
1772 return; | |
1773 | |
1774 WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl(); | |
1775 | |
1776 // This frame has no further scoping left, so it is done. Other frames might
, | |
1777 // of course, continue to scope matches. | |
1778 mainFrameImpl->m_framesScopingCount--; | |
1779 | |
1780 // If this is the last frame to finish scoping we need to trigger the final | |
1781 // update to be sent. | |
1782 if (!mainFrameImpl->m_framesScopingCount) | |
1783 mainFrameImpl->increaseMatchCount(0, identifier); | |
1784 } | |
1785 | |
1786 void WebFrameImpl::finishCurrentScopingEffort(int identifier) | |
1787 { | |
1788 flushCurrentScopingEffort(identifier); | |
1789 | |
1790 m_scopingInProgress = false; | |
1791 m_lastFindRequestCompletedWithNoMatches = !m_lastMatchCount; | |
1792 | |
1793 // This frame is done, so show any scrollbar tickmarks we haven't drawn yet. | |
1794 invalidateArea(InvalidateScrollbar); | |
1795 } | |
1796 | |
1797 void WebFrameImpl::cancelPendingScopingEffort() | |
1798 { | |
1799 deleteAllValues(m_deferredScopingWork); | |
1800 m_deferredScopingWork.clear(); | |
1801 | |
1802 m_activeMatchIndexInCurrentFrame = -1; | |
1803 | |
1804 // Last request didn't complete. | |
1805 if (m_scopingInProgress) | |
1806 m_lastFindRequestCompletedWithNoMatches = false; | |
1807 | |
1808 m_scopingInProgress = false; | |
1809 } | |
1810 | |
1811 void WebFrameImpl::increaseMatchCount(int count, int identifier) | |
1812 { | |
1813 // This function should only be called on the mainframe. | |
1814 ASSERT(!parent()); | |
1815 | |
1816 if (count) | |
1817 ++m_findMatchMarkersVersion; | |
1818 | |
1819 m_totalMatchCount += count; | |
1820 | |
1821 // Update the UI with the latest findings. | |
1822 if (client()) | |
1823 client()->reportFindInPageMatchCount(identifier, m_totalMatchCount, !m_f
ramesScopingCount); | |
1824 } | |
1825 | |
1826 void WebFrameImpl::reportFindInPageSelection(const WebRect& selectionRect, int a
ctiveMatchOrdinal, int identifier) | |
1827 { | |
1828 // Update the UI with the latest selection rect. | |
1829 if (client()) | |
1830 client()->reportFindInPageSelection(identifier, ordinalOfFirstMatchForFr
ame(this) + activeMatchOrdinal, selectionRect); | |
1831 } | |
1832 | |
1833 void WebFrameImpl::resetMatchCount() | |
1834 { | |
1835 if (m_totalMatchCount > 0) | |
1836 ++m_findMatchMarkersVersion; | |
1837 | |
1838 m_totalMatchCount = 0; | |
1839 m_framesScopingCount = 0; | |
1840 } | |
1841 | |
1842 void WebFrameImpl::sendOrientationChangeEvent(int orientation) | |
1843 { | |
1844 #if ENABLE(ORIENTATION_EVENTS) | |
1845 if (frame()) | |
1846 frame()->sendOrientationChangeEvent(orientation); | |
1847 #endif | |
1848 } | |
1849 | |
1850 void WebFrameImpl::dispatchMessageEventWithOriginCheck(const WebSecurityOrigin&
intendedTargetOrigin, const WebDOMEvent& event) | |
1851 { | |
1852 ASSERT(!event.isNull()); | |
1853 frame()->document()->domWindow()->dispatchMessageEventWithOriginCheck(intend
edTargetOrigin.get(), event, 0); | |
1854 } | |
1855 | |
1856 int WebFrameImpl::findMatchMarkersVersion() const | |
1857 { | |
1858 ASSERT(!parent()); | |
1859 return m_findMatchMarkersVersion; | |
1860 } | |
1861 | |
1862 void WebFrameImpl::clearFindMatchesCache() | |
1863 { | |
1864 if (!m_findMatchesCache.isEmpty()) | |
1865 viewImpl()->mainFrameImpl()->m_findMatchMarkersVersion++; | |
1866 | |
1867 m_findMatchesCache.clear(); | |
1868 m_findMatchRectsAreValid = false; | |
1869 } | |
1870 | |
1871 bool WebFrameImpl::isActiveMatchFrameValid() const | |
1872 { | |
1873 WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl(); | |
1874 WebFrameImpl* activeMatchFrame = mainFrameImpl->activeMatchFrame(); | |
1875 return activeMatchFrame && activeMatchFrame->m_activeMatch && activeMatchFra
me->frame()->tree()->isDescendantOf(mainFrameImpl->frame()); | |
1876 } | |
1877 | |
1878 void WebFrameImpl::updateFindMatchRects() | |
1879 { | |
1880 IntSize currentContentsSize = contentsSize(); | |
1881 if (m_contentsSizeForCurrentFindMatchRects != currentContentsSize) { | |
1882 m_contentsSizeForCurrentFindMatchRects = currentContentsSize; | |
1883 m_findMatchRectsAreValid = false; | |
1884 } | |
1885 | |
1886 size_t deadMatches = 0; | |
1887 for (Vector<FindMatch>::iterator it = m_findMatchesCache.begin(); it != m_fi
ndMatchesCache.end(); ++it) { | |
1888 if (!it->m_range->boundaryPointsValid() || !it->m_range->startContainer(
)->inDocument()) | |
1889 it->m_rect = FloatRect(); | |
1890 else if (!m_findMatchRectsAreValid) | |
1891 it->m_rect = findInPageRectFromRange(it->m_range.get()); | |
1892 | |
1893 if (it->m_rect.isEmpty()) | |
1894 ++deadMatches; | |
1895 } | |
1896 | |
1897 // Remove any invalid matches from the cache. | |
1898 if (deadMatches) { | |
1899 Vector<FindMatch> filteredMatches; | |
1900 filteredMatches.reserveCapacity(m_findMatchesCache.size() - deadMatches)
; | |
1901 | |
1902 for (Vector<FindMatch>::const_iterator it = m_findMatchesCache.begin();
it != m_findMatchesCache.end(); ++it) | |
1903 if (!it->m_rect.isEmpty()) | |
1904 filteredMatches.append(*it); | |
1905 | |
1906 m_findMatchesCache.swap(filteredMatches); | |
1907 } | |
1908 | |
1909 // Invalidate the rects in child frames. Will be updated later during traver
sal. | |
1910 if (!m_findMatchRectsAreValid) | |
1911 for (WebFrame* child = firstChild(); child; child = child->nextSibling()
) | |
1912 static_cast<WebFrameImpl*>(child)->m_findMatchRectsAreValid = false; | |
1913 | |
1914 m_findMatchRectsAreValid = true; | |
1915 } | |
1916 | |
1917 WebFloatRect WebFrameImpl::activeFindMatchRect() | |
1918 { | |
1919 ASSERT(!parent()); | |
1920 | |
1921 if (!isActiveMatchFrameValid()) | |
1922 return WebFloatRect(); | |
1923 | |
1924 return WebFloatRect(findInPageRectFromRange(m_currentActiveMatchFrame->m_act
iveMatch.get())); | |
1925 } | |
1926 | |
1927 void WebFrameImpl::findMatchRects(WebVector<WebFloatRect>& outputRects) | |
1928 { | |
1929 ASSERT(!parent()); | |
1930 | |
1931 Vector<WebFloatRect> matchRects; | |
1932 for (WebFrameImpl* frame = this; frame; frame = static_cast<WebFrameImpl*>(f
rame->traverseNext(false))) | |
1933 frame->appendFindMatchRects(matchRects); | |
1934 | |
1935 outputRects = matchRects; | |
1936 } | |
1937 | |
1938 void WebFrameImpl::appendFindMatchRects(Vector<WebFloatRect>& frameRects) | |
1939 { | |
1940 updateFindMatchRects(); | |
1941 frameRects.reserveCapacity(frameRects.size() + m_findMatchesCache.size()); | |
1942 for (Vector<FindMatch>::const_iterator it = m_findMatchesCache.begin(); it !
= m_findMatchesCache.end(); ++it) { | |
1943 ASSERT(!it->m_rect.isEmpty()); | |
1944 frameRects.append(it->m_rect); | |
1945 } | |
1946 } | |
1947 | |
1948 int WebFrameImpl::selectNearestFindMatch(const WebFloatPoint& point, WebRect* se
lectionRect) | |
1949 { | |
1950 ASSERT(!parent()); | |
1951 | |
1952 WebFrameImpl* bestFrame = 0; | |
1953 int indexInBestFrame = -1; | |
1954 float distanceInBestFrame = FLT_MAX; | |
1955 | |
1956 for (WebFrameImpl* frame = this; frame; frame = static_cast<WebFrameImpl*>(f
rame->traverseNext(false))) { | |
1957 float distanceInFrame; | |
1958 int indexInFrame = frame->nearestFindMatch(point, distanceInFrame); | |
1959 if (distanceInFrame < distanceInBestFrame) { | |
1960 bestFrame = frame; | |
1961 indexInBestFrame = indexInFrame; | |
1962 distanceInBestFrame = distanceInFrame; | |
1963 } | |
1964 } | |
1965 | |
1966 if (indexInBestFrame != -1) | |
1967 return bestFrame->selectFindMatch(static_cast<unsigned>(indexInBestFrame
), selectionRect); | |
1968 | |
1969 return -1; | |
1970 } | |
1971 | |
1972 int WebFrameImpl::nearestFindMatch(const FloatPoint& point, float& distanceSquar
ed) | |
1973 { | |
1974 updateFindMatchRects(); | |
1975 | |
1976 int nearest = -1; | |
1977 distanceSquared = FLT_MAX; | |
1978 for (size_t i = 0; i < m_findMatchesCache.size(); ++i) { | |
1979 ASSERT(!m_findMatchesCache[i].m_rect.isEmpty()); | |
1980 FloatSize offset = point - m_findMatchesCache[i].m_rect.center(); | |
1981 float width = offset.width(); | |
1982 float height = offset.height(); | |
1983 float currentDistanceSquared = width * width + height * height; | |
1984 if (currentDistanceSquared < distanceSquared) { | |
1985 nearest = i; | |
1986 distanceSquared = currentDistanceSquared; | |
1987 } | |
1988 } | |
1989 return nearest; | |
1990 } | |
1991 | |
1992 int WebFrameImpl::selectFindMatch(unsigned index, WebRect* selectionRect) | |
1993 { | |
1994 ASSERT_WITH_SECURITY_IMPLICATION(index < m_findMatchesCache.size()); | |
1995 | |
1996 RefPtr<Range> range = m_findMatchesCache[index].m_range; | |
1997 if (!range->boundaryPointsValid() || !range->startContainer()->inDocument()) | |
1998 return -1; | |
1999 | |
2000 // Check if the match is already selected. | |
2001 WebFrameImpl* activeMatchFrame = viewImpl()->mainFrameImpl()->m_currentActiv
eMatchFrame; | |
2002 if (this != activeMatchFrame || !m_activeMatch || !areRangesEqual(m_activeMa
tch.get(), range.get())) { | |
2003 if (isActiveMatchFrameValid()) | |
2004 activeMatchFrame->setMarkerActive(activeMatchFrame->m_activeMatch.ge
t(), false); | |
2005 | |
2006 m_activeMatchIndexInCurrentFrame = m_findMatchesCache[index].m_ordinal -
1; | |
2007 | |
2008 // Set this frame as the active frame (the one with the active highlight
). | |
2009 viewImpl()->mainFrameImpl()->m_currentActiveMatchFrame = this; | |
2010 viewImpl()->setFocusedFrame(this); | |
2011 | |
2012 m_activeMatch = range.release(); | |
2013 setMarkerActive(m_activeMatch.get(), true); | |
2014 | |
2015 // Clear any user selection, to make sure Find Next continues on from th
e match we just activated. | |
2016 frame()->selection()->clear(); | |
2017 | |
2018 // Make sure no node is focused. See http://crbug.com/38700. | |
2019 frame()->document()->setFocusedNode(0); | |
2020 } | |
2021 | |
2022 IntRect activeMatchRect; | |
2023 IntRect activeMatchBoundingBox = enclosingIntRect(RenderObject::absoluteBoun
dingBoxRectForRange(m_activeMatch.get())); | |
2024 | |
2025 if (!activeMatchBoundingBox.isEmpty()) { | |
2026 if (m_activeMatch->firstNode() && m_activeMatch->firstNode()->renderer()
) | |
2027 m_activeMatch->firstNode()->renderer()->scrollRectToVisible(activeMa
tchBoundingBox, | |
2028 ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::align
CenterIfNeeded); | |
2029 | |
2030 // Zoom to the active match. | |
2031 activeMatchRect = frameView()->contentsToWindow(activeMatchBoundingBox); | |
2032 viewImpl()->zoomToFindInPageRect(activeMatchRect); | |
2033 } | |
2034 | |
2035 if (selectionRect) | |
2036 *selectionRect = activeMatchRect; | |
2037 | |
2038 return ordinalOfFirstMatchForFrame(this) + m_activeMatchIndexInCurrentFrame
+ 1; | |
2039 } | |
2040 | |
2041 WebString WebFrameImpl::contentAsText(size_t maxChars) const | |
2042 { | |
2043 if (!frame()) | |
2044 return WebString(); | |
2045 Vector<UChar> text; | |
2046 frameContentAsPlainText(maxChars, frame(), &text); | |
2047 return String::adopt(text); | |
2048 } | |
2049 | |
2050 WebString WebFrameImpl::contentAsMarkup() const | |
2051 { | |
2052 if (!frame()) | |
2053 return WebString(); | |
2054 return createFullMarkup(frame()->document()); | |
2055 } | |
2056 | |
2057 WebString WebFrameImpl::renderTreeAsText(RenderAsTextControls toShow) const | |
2058 { | |
2059 RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal; | |
2060 | |
2061 if (toShow & RenderAsTextDebug) | |
2062 behavior |= RenderAsTextShowCompositedLayers | RenderAsTextShowAddresses
| RenderAsTextShowIDAndClass | RenderAsTextShowLayerNesting; | |
2063 | |
2064 if (toShow & RenderAsTextPrinting) | |
2065 behavior |= RenderAsTextPrintingMode; | |
2066 | |
2067 return externalRepresentation(frame(), behavior); | |
2068 } | |
2069 | |
2070 WebString WebFrameImpl::markerTextForListItem(const WebElement& webElement) cons
t | |
2071 { | |
2072 return WebCore::markerTextForListItem(const_cast<Element*>(webElement.constU
nwrap<Element>())); | |
2073 } | |
2074 | |
2075 void WebFrameImpl::printPagesWithBoundaries(WebCanvas* canvas, const WebSize& pa
geSizeInPixels) | |
2076 { | |
2077 ASSERT(m_printContext); | |
2078 | |
2079 GraphicsContext graphicsContext(canvas); | |
2080 graphicsContext.setPrinting(true); | |
2081 | |
2082 m_printContext->spoolAllPagesWithBoundaries(graphicsContext, FloatSize(pageS
izeInPixels.width, pageSizeInPixels.height)); | |
2083 } | |
2084 | |
2085 WebRect WebFrameImpl::selectionBoundsRect() const | |
2086 { | |
2087 return hasSelection() ? WebRect(IntRect(frame()->selection()->bounds(false))
) : WebRect(); | |
2088 } | |
2089 | |
2090 bool WebFrameImpl::selectionStartHasSpellingMarkerFor(int from, int length) cons
t | |
2091 { | |
2092 if (!frame()) | |
2093 return false; | |
2094 return frame()->editor()->selectionStartHasMarkerFor(DocumentMarker::Spellin
g, from, length); | |
2095 } | |
2096 | |
2097 WebString WebFrameImpl::layerTreeAsText(bool showDebugInfo) const | |
2098 { | |
2099 if (!frame()) | |
2100 return WebString(); | |
2101 | |
2102 return WebString(frame()->layerTreeAsText(showDebugInfo ? LayerTreeIncludesD
ebugInfo : LayerTreeNormal)); | |
2103 } | |
2104 | |
2105 // WebFrameImpl public --------------------------------------------------------- | |
2106 | |
2107 PassRefPtr<WebFrameImpl> WebFrameImpl::create(WebFrameClient* client) | |
2108 { | |
2109 return adoptRef(new WebFrameImpl(client)); | |
2110 } | |
2111 | |
2112 WebFrameImpl::WebFrameImpl(WebFrameClient* client) | |
2113 : FrameDestructionObserver(0) | |
2114 , m_frameLoaderClient(this) | |
2115 , m_client(client) | |
2116 , m_currentActiveMatchFrame(0) | |
2117 , m_activeMatchIndexInCurrentFrame(-1) | |
2118 , m_locatingActiveRect(false) | |
2119 , m_resumeScopingFromRange(0) | |
2120 , m_lastMatchCount(-1) | |
2121 , m_totalMatchCount(-1) | |
2122 , m_framesScopingCount(-1) | |
2123 , m_findRequestIdentifier(-1) | |
2124 , m_scopingInProgress(false) | |
2125 , m_lastFindRequestCompletedWithNoMatches(false) | |
2126 , m_nextInvalidateAfter(0) | |
2127 , m_findMatchMarkersVersion(0) | |
2128 , m_findMatchRectsAreValid(false) | |
2129 , m_identifier(generateFrameIdentifier()) | |
2130 , m_inSameDocumentHistoryLoad(false) | |
2131 { | |
2132 WebKit::Platform::current()->incrementStatsCounter(webFrameActiveCount); | |
2133 frameCount++; | |
2134 } | |
2135 | |
2136 WebFrameImpl::~WebFrameImpl() | |
2137 { | |
2138 WebKit::Platform::current()->decrementStatsCounter(webFrameActiveCount); | |
2139 frameCount--; | |
2140 | |
2141 cancelPendingScopingEffort(); | |
2142 } | |
2143 | |
2144 void WebFrameImpl::setWebCoreFrame(WebCore::Frame* frame) | |
2145 { | |
2146 ASSERT(frame); | |
2147 observeFrame(frame); | |
2148 } | |
2149 | |
2150 void WebFrameImpl::initializeAsMainFrame(WebCore::Page* page) | |
2151 { | |
2152 RefPtr<Frame> mainFrame = Frame::create(page, 0, &m_frameLoaderClient); | |
2153 setWebCoreFrame(mainFrame.get()); | |
2154 | |
2155 // Add reference on behalf of FrameLoader. See comments in | |
2156 // WebFrameLoaderClient::frameLoaderDestroyed for more info. | |
2157 ref(); | |
2158 | |
2159 // We must call init() after m_frame is assigned because it is referenced | |
2160 // during init(). | |
2161 frame()->init(); | |
2162 } | |
2163 | |
2164 PassRefPtr<Frame> WebFrameImpl::createChildFrame(const FrameLoadRequest& request
, HTMLFrameOwnerElement* ownerElement) | |
2165 { | |
2166 RefPtr<WebFrameImpl> webframe(adoptRef(new WebFrameImpl(m_client))); | |
2167 | |
2168 // Add an extra ref on behalf of the Frame/FrameLoader, which references the | |
2169 // WebFrame via the FrameLoaderClient interface. See the comment at the top | |
2170 // of this file for more info. | |
2171 webframe->ref(); | |
2172 | |
2173 RefPtr<Frame> childFrame = Frame::create(frame()->page(), ownerElement, &web
frame->m_frameLoaderClient); | |
2174 webframe->setWebCoreFrame(childFrame.get()); | |
2175 | |
2176 childFrame->tree()->setName(request.frameName()); | |
2177 | |
2178 frame()->tree()->appendChild(childFrame); | |
2179 | |
2180 // Frame::init() can trigger onload event in the parent frame, | |
2181 // which may detach this frame and trigger a null-pointer access | |
2182 // in FrameTree::removeChild. Move init() after appendChild call | |
2183 // so that webframe->mFrame is in the tree before triggering | |
2184 // onload event handler. | |
2185 // Because the event handler may set webframe->mFrame to null, | |
2186 // it is necessary to check the value after calling init() and | |
2187 // return without loading URL. | |
2188 // (b:791612) | |
2189 childFrame->init(); // create an empty document | |
2190 if (!childFrame->tree()->parent()) | |
2191 return 0; | |
2192 | |
2193 frame()->loader()->loadURLIntoChildFrame(request.resourceRequest(), childFra
me.get()); | |
2194 | |
2195 // A synchronous navigation (about:blank) would have already processed | |
2196 // onload, so it is possible for the frame to have already been destroyed by | |
2197 // script in the page. | |
2198 if (!childFrame->tree()->parent()) | |
2199 return 0; | |
2200 | |
2201 if (m_client) | |
2202 m_client->didCreateFrame(this, webframe.get()); | |
2203 | |
2204 return childFrame.release(); | |
2205 } | |
2206 | |
2207 void WebFrameImpl::didChangeContentsSize(const IntSize& size) | |
2208 { | |
2209 // This is only possible on the main frame. | |
2210 if (m_totalMatchCount > 0) { | |
2211 ASSERT(!parent()); | |
2212 ++m_findMatchMarkersVersion; | |
2213 } | |
2214 } | |
2215 | |
2216 void WebFrameImpl::createFrameView() | |
2217 { | |
2218 TRACE_EVENT0("webkit", "WebFrameImpl::createFrameView"); | |
2219 | |
2220 ASSERT(frame()); // If frame() doesn't exist, we probably didn't init proper
ly. | |
2221 | |
2222 WebViewImpl* webView = viewImpl(); | |
2223 bool isMainFrame = webView->mainFrameImpl()->frame() == frame(); | |
2224 if (isMainFrame) | |
2225 webView->suppressInvalidations(true); | |
2226 | |
2227 frame()->createView(webView->size(), Color::white, webView->isTransparent(),
webView->fixedLayoutSize(), isMainFrame ? webView->isFixedLayoutModeEnabled() :
0); | |
2228 if (webView->shouldAutoResize() && isMainFrame) | |
2229 frame()->view()->enableAutoSizeMode(true, webView->minAutoSize(), webVie
w->maxAutoSize()); | |
2230 | |
2231 if (isMainFrame) | |
2232 webView->suppressInvalidations(false); | |
2233 | |
2234 if (isMainFrame && webView->devToolsAgentPrivate()) | |
2235 webView->devToolsAgentPrivate()->mainFrameViewCreated(this); | |
2236 } | |
2237 | |
2238 WebFrameImpl* WebFrameImpl::fromFrame(Frame* frame) | |
2239 { | |
2240 if (!frame) | |
2241 return 0; | |
2242 return static_cast<FrameLoaderClientImpl*>(frame->loader()->client())->webFr
ame(); | |
2243 } | |
2244 | |
2245 WebFrameImpl* WebFrameImpl::fromFrameOwnerElement(Element* element) | |
2246 { | |
2247 // FIXME: Why do we check specifically for <iframe> and <frame> here? Why ca
n't we get the WebFrameImpl from an <object> element, for example. | |
2248 if (!element || !element->isFrameOwnerElement() || (!element->hasTagName(HTM
LNames::iframeTag) && !element->hasTagName(HTMLNames::frameTag))) | |
2249 return 0; | |
2250 HTMLFrameOwnerElement* frameElement = toFrameOwnerElement(element); | |
2251 return fromFrame(frameElement->contentFrame()); | |
2252 } | |
2253 | |
2254 WebViewImpl* WebFrameImpl::viewImpl() const | |
2255 { | |
2256 if (!frame()) | |
2257 return 0; | |
2258 return WebViewImpl::fromPage(frame()->page()); | |
2259 } | |
2260 | |
2261 WebDataSourceImpl* WebFrameImpl::dataSourceImpl() const | |
2262 { | |
2263 return static_cast<WebDataSourceImpl*>(dataSource()); | |
2264 } | |
2265 | |
2266 WebDataSourceImpl* WebFrameImpl::provisionalDataSourceImpl() const | |
2267 { | |
2268 return static_cast<WebDataSourceImpl*>(provisionalDataSource()); | |
2269 } | |
2270 | |
2271 void WebFrameImpl::setFindEndstateFocusAndSelection() | |
2272 { | |
2273 WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl(); | |
2274 | |
2275 if (this == mainFrameImpl->activeMatchFrame() && m_activeMatch.get()) { | |
2276 // If the user has set the selection since the match was found, we | |
2277 // don't focus anything. | |
2278 VisibleSelection selection(frame()->selection()->selection()); | |
2279 if (!selection.isNone()) | |
2280 return; | |
2281 | |
2282 // Try to find the first focusable node up the chain, which will, for | |
2283 // example, focus links if we have found text within the link. | |
2284 Node* node = m_activeMatch->firstNode(); | |
2285 if (node && node->isInShadowTree()) { | |
2286 Node* host = node->deprecatedShadowAncestorNode(); | |
2287 if (host->hasTagName(HTMLNames::inputTag) || host->hasTagName(HTMLNa
mes::textareaTag)) | |
2288 node = host; | |
2289 } | |
2290 while (node && !node->isFocusable() && node != frame()->document()) | |
2291 node = node->parentNode(); | |
2292 | |
2293 if (node && node != frame()->document()) { | |
2294 // Found a focusable parent node. Set the active match as the | |
2295 // selection and focus to the focusable node. | |
2296 frame()->selection()->setSelection(m_activeMatch.get()); | |
2297 frame()->document()->setFocusedNode(node); | |
2298 return; | |
2299 } | |
2300 | |
2301 // Iterate over all the nodes in the range until we find a focusable nod
e. | |
2302 // This, for example, sets focus to the first link if you search for | |
2303 // text and text that is within one or more links. | |
2304 node = m_activeMatch->firstNode(); | |
2305 while (node && node != m_activeMatch->pastLastNode()) { | |
2306 if (node->isFocusable()) { | |
2307 frame()->document()->setFocusedNode(node); | |
2308 return; | |
2309 } | |
2310 node = NodeTraversal::next(node); | |
2311 } | |
2312 | |
2313 // No node related to the active match was focusable, so set the | |
2314 // active match as the selection (so that when you end the Find session, | |
2315 // you'll have the last thing you found highlighted) and make sure that | |
2316 // we have nothing focused (otherwise you might have text selected but | |
2317 // a link focused, which is weird). | |
2318 frame()->selection()->setSelection(m_activeMatch.get()); | |
2319 frame()->document()->setFocusedNode(0); | |
2320 | |
2321 // Finally clear the active match, for two reasons: | |
2322 // We just finished the find 'session' and we don't want future (potenti
ally | |
2323 // unrelated) find 'sessions' operations to start at the same place. | |
2324 // The WebFrameImpl could get reused and the m_activeMatch could end up
pointing | |
2325 // to a document that is no longer valid. Keeping an invalid reference a
round | |
2326 // is just asking for trouble. | |
2327 m_activeMatch = 0; | |
2328 } | |
2329 } | |
2330 | |
2331 void WebFrameImpl::didFail(const ResourceError& error, bool wasProvisional) | |
2332 { | |
2333 if (!client()) | |
2334 return; | |
2335 WebURLError webError = error; | |
2336 if (wasProvisional) | |
2337 client()->didFailProvisionalLoad(this, webError); | |
2338 else | |
2339 client()->didFailLoad(this, webError); | |
2340 } | |
2341 | |
2342 void WebFrameImpl::setCanHaveScrollbars(bool canHaveScrollbars) | |
2343 { | |
2344 frame()->view()->setCanHaveScrollbars(canHaveScrollbars); | |
2345 } | |
2346 | |
2347 void WebFrameImpl::invalidateArea(AreaToInvalidate area) | |
2348 { | |
2349 ASSERT(frame() && frame()->view()); | |
2350 FrameView* view = frame()->view(); | |
2351 | |
2352 if ((area & InvalidateAll) == InvalidateAll) | |
2353 view->invalidateRect(view->frameRect()); | |
2354 else { | |
2355 if ((area & InvalidateContentArea) == InvalidateContentArea) { | |
2356 IntRect contentArea( | |
2357 view->x(), view->y(), view->visibleWidth(), view->visibleHeight(
)); | |
2358 IntRect frameRect = view->frameRect(); | |
2359 contentArea.move(-frameRect.x(), -frameRect.y()); | |
2360 view->invalidateRect(contentArea); | |
2361 } | |
2362 } | |
2363 | |
2364 if ((area & InvalidateScrollbar) == InvalidateScrollbar) { | |
2365 // Invalidate the vertical scroll bar region for the view. | |
2366 Scrollbar* scrollbar = view->verticalScrollbar(); | |
2367 if (scrollbar) | |
2368 scrollbar->invalidate(); | |
2369 } | |
2370 } | |
2371 | |
2372 void WebFrameImpl::addMarker(Range* range, bool activeMatch) | |
2373 { | |
2374 frame()->document()->markers()->addTextMatchMarker(range, activeMatch); | |
2375 } | |
2376 | |
2377 void WebFrameImpl::setMarkerActive(Range* range, bool active) | |
2378 { | |
2379 WebCore::ExceptionCode ec; | |
2380 if (!range || range->collapsed(ec)) | |
2381 return; | |
2382 frame()->document()->markers()->setMarkersActive(range, active); | |
2383 } | |
2384 | |
2385 int WebFrameImpl::ordinalOfFirstMatchForFrame(WebFrameImpl* frame) const | |
2386 { | |
2387 int ordinal = 0; | |
2388 WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl(); | |
2389 // Iterate from the main frame up to (but not including) |frame| and | |
2390 // add up the number of matches found so far. | |
2391 for (WebFrameImpl* it = mainFrameImpl; it != frame; it = static_cast<WebFram
eImpl*>(it->traverseNext(true))) { | |
2392 if (it->m_lastMatchCount > 0) | |
2393 ordinal += it->m_lastMatchCount; | |
2394 } | |
2395 return ordinal; | |
2396 } | |
2397 | |
2398 bool WebFrameImpl::shouldScopeMatches(const String& searchText) | |
2399 { | |
2400 // Don't scope if we can't find a frame or a view. | |
2401 // The user may have closed the tab/application, so abort. | |
2402 // Also ignore detached frames, as many find operations report to the main f
rame. | |
2403 if (!frame() || !frame()->view() || !frame()->page() || !hasVisibleContent()
) | |
2404 return false; | |
2405 | |
2406 ASSERT(frame()->document() && frame()->view()); | |
2407 | |
2408 // If the frame completed the scoping operation and found 0 matches the last | |
2409 // time it was searched, then we don't have to search it again if the user i
s | |
2410 // just adding to the search string or sending the same search string again. | |
2411 if (m_lastFindRequestCompletedWithNoMatches && !m_lastSearchString.isEmpty()
) { | |
2412 // Check to see if the search string prefixes match. | |
2413 String previousSearchPrefix = | |
2414 searchText.substring(0, m_lastSearchString.length()); | |
2415 | |
2416 if (previousSearchPrefix == m_lastSearchString) | |
2417 return false; // Don't search this frame, it will be fruitless. | |
2418 } | |
2419 | |
2420 return true; | |
2421 } | |
2422 | |
2423 void WebFrameImpl::scopeStringMatchesSoon(int identifier, const WebString& searc
hText, const WebFindOptions& options, bool reset) | |
2424 { | |
2425 m_deferredScopingWork.append(new DeferredScopeStringMatches(this, identifier
, searchText, options, reset)); | |
2426 } | |
2427 | |
2428 void WebFrameImpl::callScopeStringMatches(DeferredScopeStringMatches* caller, in
t identifier, const WebString& searchText, const WebFindOptions& options, bool r
eset) | |
2429 { | |
2430 m_deferredScopingWork.remove(m_deferredScopingWork.find(caller)); | |
2431 scopeStringMatches(identifier, searchText, options, reset); | |
2432 | |
2433 // This needs to happen last since searchText is passed by reference. | |
2434 delete caller; | |
2435 } | |
2436 | |
2437 void WebFrameImpl::invalidateIfNecessary() | |
2438 { | |
2439 if (m_lastMatchCount <= m_nextInvalidateAfter) | |
2440 return; | |
2441 | |
2442 // FIXME: (http://b/1088165) Optimize the drawing of the tickmarks and | |
2443 // remove this. This calculation sets a milestone for when next to | |
2444 // invalidate the scrollbar and the content area. We do this so that we | |
2445 // don't spend too much time drawing the scrollbar over and over again. | |
2446 // Basically, up until the first 500 matches there is no throttle. | |
2447 // After the first 500 matches, we set set the milestone further and | |
2448 // further out (750, 1125, 1688, 2K, 3K). | |
2449 static const int startSlowingDownAfter = 500; | |
2450 static const int slowdown = 750; | |
2451 | |
2452 int i = m_lastMatchCount / startSlowingDownAfter; | |
2453 m_nextInvalidateAfter += i * slowdown; | |
2454 invalidateArea(InvalidateScrollbar); | |
2455 } | |
2456 | |
2457 void WebFrameImpl::loadJavaScriptURL(const KURL& url) | |
2458 { | |
2459 // This is copied from ScriptController::executeScriptIfJavaScriptURL. | |
2460 // Unfortunately, we cannot just use that method since it is private, and | |
2461 // it also doesn't quite behave as we require it to for bookmarklets. The | |
2462 // key difference is that we need to suppress loading the string result | |
2463 // from evaluating the JS URL if executing the JS URL resulted in a | |
2464 // location change. We also allow a JS URL to be loaded even if scripts on | |
2465 // the page are otherwise disabled. | |
2466 | |
2467 if (!frame()->document() || !frame()->page()) | |
2468 return; | |
2469 | |
2470 RefPtr<Document> ownerDocument(frame()->document()); | |
2471 | |
2472 // Protect privileged pages against bookmarklets and other javascript manipu
lations. | |
2473 if (SchemeRegistry::shouldTreatURLSchemeAsNotAllowingJavascriptURLs(frame()-
>document()->url().protocol())) | |
2474 return; | |
2475 | |
2476 String script = decodeURLEscapeSequences(url.string().substring(strlen("java
script:"))); | |
2477 ScriptValue result = frame()->script()->executeScript(script, true); | |
2478 | |
2479 String scriptResult; | |
2480 if (!result.getString(scriptResult)) | |
2481 return; | |
2482 | |
2483 if (!frame()->navigationScheduler()->locationChangePending()) | |
2484 frame()->document()->loader()->replaceDocument(scriptResult, ownerDocume
nt.get()); | |
2485 } | |
2486 | |
2487 void WebFrameImpl::willDetachPage() | |
2488 { | |
2489 if (!frame() || !frame()->page()) | |
2490 return; | |
2491 | |
2492 // Do not expect string scoping results from any frames that got detached | |
2493 // in the middle of the operation. | |
2494 if (m_scopingInProgress) { | |
2495 | |
2496 // There is a possibility that the frame being detached was the only | |
2497 // pending one. We need to make sure final replies can be sent. | |
2498 flushCurrentScopingEffort(m_findRequestIdentifier); | |
2499 | |
2500 cancelPendingScopingEffort(); | |
2501 } | |
2502 } | |
2503 | |
2504 } // namespace WebKit | |
OLD | NEW |