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