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