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