KWQKHTMLPart.mm 17.7 KB
Newer Older
rjw's avatar
rjw committed
1
/*
2
 * Copyright (C) 2001, 2002 Apple Computer, Inc.  All rights reserved.
rjw's avatar
rjw committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */
25

26
#import "KWQKHTMLPart.h"
27

darin's avatar
darin committed
28 29 30 31 32 33 34
#import "htmltokenizer.h"
#import "html_documentimpl.h"
#import "render_root.h"
#import "render_frames.h"
#import "render_text.h"
#import "khtmlpart_p.h"
#import "khtmlview.h"
35

darin's avatar
darin committed
36
#import "WebCoreBridge.h"
mjs's avatar
mjs committed
37
#import "WebCoreBridgePrivate.h"
darin's avatar
darin committed
38
#import "WebCoreViewFactory.h"
rjw's avatar
rjw committed
39

40
#import "KWQDummyView.h"
darin's avatar
darin committed
41
#import "KWQKJobClasses.h"
darin's avatar
darin committed
42
#import "KWQLogging.h"
43

44 45
#undef _KWQ_TIMING

46
using khtml::Cache;
darin's avatar
darin committed
47
using khtml::ChildFrame;
48 49 50
using khtml::Decoder;
using khtml::RenderObject;
using khtml::RenderPart;
darin's avatar
darin committed
51
using khtml::RenderText;
52
using khtml::RenderWidget;
53

54 55
using KIO::Job;

darin's avatar
darin committed
56
using KParts::ReadOnlyPart;
57 58
using KParts::URLArgs;

59
void KHTMLPart::completed()
darin's avatar
darin committed
60
{
61
    kwq->_completed.call();
darin's avatar
darin committed
62 63
}

64
void KHTMLPart::completed(bool arg)
darin's avatar
darin committed
65
{
66
    kwq->_completed.call(arg);
darin's avatar
darin committed
67 68
}

69
void KHTMLPart::nodeActivated(const DOM::Node &aNode)
darin's avatar
darin committed
70 71 72
{
}

73
void KHTMLPart::onURL(const QString &)
darin's avatar
darin committed
74 75 76
{
}

77
void KHTMLPart::setStatusBarText(const QString &status)
78
{
79
    kwq->setStatusBarText(status);
80 81
}

82
void KHTMLPart::started(Job *j)
83
{
84
    kwq->_started.call(j);
85 86
}

87
static void redirectionTimerMonitor(void *context)
88
{
89 90
    KWQKHTMLPart *kwq = static_cast<KWQKHTMLPart *>(context);
    kwq->redirectionTimerStartedOrStopped();
91 92
}

93
KWQKHTMLPart::KWQKHTMLPart(KHTMLPart *p)
darin's avatar
darin committed
94
    : part(p), d(part->d)
95 96 97
    , _started(p, SIGNAL(started(KIO::Job *)))
    , _completed(p, SIGNAL(completed()))
    , _completedWithBool(p, SIGNAL(completed(bool)))
98
    , _needsToSetWidgetsAside(false)
99 100
    , _ownsView(false)
    , _currentEvent(nil)
101
{
102
    Cache::init();
103
    mutableInstances().prepend(this);
darin's avatar
darin committed
104
    d->m_redirectionTimer.setMonitor(redirectionTimerMonitor, this);
105
}
rjw's avatar
rjw committed
106

107
KWQKHTMLPart::~KWQKHTMLPart()
darin's avatar
darin committed
108
{
109
    mutableInstances().remove(this);
110 111 112 113
    if (_ownsView) {
        delete d->m_view;
    }
    [_currentEvent release];
darin's avatar
darin committed
114 115
}

116
WebCoreBridge *KWQKHTMLPart::bridgeForFrameName(const QString &frameName)
mjs's avatar
mjs committed
117
{
darin's avatar
darin committed
118
    WebCoreBridge *frame;
119 120 121
    if (frameName.isEmpty()) {
        // If we're the only frame in a frameset then pop the frame.
        KHTMLPart *parentPart = part->parentPart();
122
        frame = parentPart ? parentPart->kwq->_bridge : nil;
123
        if ([[frame childFrames] count] != 1) {
124
            frame = _bridge;
darin's avatar
darin committed
125 126
        }
    } else {
cblu's avatar
cblu committed
127
        frame = [_bridge findOrCreateFramedNamed:frameName.getNSString()];
darin's avatar
darin committed
128
    }
129 130 131
    
    return frame;
}
mjs's avatar
mjs committed
132

133 134 135 136 137
QString KWQKHTMLPart::generateFrameName()
{
    return QString::fromNSString([_bridge generateFrameName]);
}

138
void KWQKHTMLPart::openURL(const KURL &url)
139
{
140 141 142
    // FIXME: The lack of args here to get the reload flag from
    // indicates a problem in how we use KHTMLPart::processObjectRequest,
    // where we are opening the URL before the args are set up.
darin's avatar
darin committed
143
    [_bridge loadURL:url.url().getNSString() reload:NO triggeringEvent:nil isFormSubmission:NO];
144
}
145

146
void KWQKHTMLPart::openURLRequest(const KURL &url, const URLArgs &args)
147
{
darin's avatar
darin committed
148
    [bridgeForFrameName(args.frameName) loadURL:url.url().getNSString() reload:args.reload triggeringEvent:nil isFormSubmission:NO];
mjs's avatar
mjs committed
149 150 151 152
}

void KWQKHTMLPart::submitForm(const KURL &url, const URLArgs &args)
{
153
    if (!args.doPost()) {
darin's avatar
darin committed
154
        [bridgeForFrameName(args.frameName) loadURL:url.url().getNSString() reload:args.reload triggeringEvent:_currentEvent isFormSubmission:YES];
155 156 157
    } else {
        QString contentType = args.contentType();
        ASSERT(contentType.startsWith("Content-Type: "));
darin's avatar
darin committed
158
        [bridgeForFrameName(args.frameName) postWithURL:url.url().getNSString()
159
            data:[NSData dataWithBytes:args.postData.data() length:args.postData.size()]
mjs's avatar
mjs committed
160
            contentType:contentType.mid(14).getNSString() triggeringEvent:_currentEvent];
161
    }
mjs's avatar
mjs committed
162 163
}

164
void KWQKHTMLPart::slotData(NSString *encoding, bool forceEncoding, const char *bytes, int length, bool complete)
165 166
{
    if (!d->m_workingURL.isEmpty()) {
darin's avatar
darin committed
167
        part->receivedFirstData();
168
    }
169
    
170 171 172
    ASSERT(d->m_doc);
    ASSERT(d->m_doc->parsing());
    
173 174
    if (encoding) {
        part->setEncoding(QString::fromNSString(encoding), forceEncoding);
mjs's avatar
mjs committed
175 176
    } else {
        part->setEncoding(QString::null, false);
177
    }
178
    
darin's avatar
darin committed
179
    part->write(bytes, length);
180
}
rjw's avatar
rjw committed
181

182
void KWQKHTMLPart::urlSelected(const KURL &url, int button, int state, const URLArgs &args)
rjw's avatar
rjw committed
183
{
darin's avatar
darin committed
184
    [bridgeForFrameName(args.frameName) loadURL:url.url().getNSString() reload:args.reload triggeringEvent:_currentEvent isFormSubmission:NO];
185 186
}

darin's avatar
darin committed
187 188 189 190 191 192
class KWQPluginPart : public ReadOnlyPart
{
    virtual bool openURL(const KURL &) { return true; }
    virtual bool closeURL() { return true; }
};

193
ReadOnlyPart *KWQKHTMLPart::createPart(const ChildFrame &child, const KURL &url, const QString &mimeType)
194
{
darin's avatar
darin committed
195
    if (child.m_type == ChildFrame::Object) {
cblu's avatar
cblu committed
196
        NSMutableArray *attributesArray = [NSMutableArray arrayWithCapacity:child.m_params.count()];
darin's avatar
darin committed
197
        for (uint i = 0; i < child.m_params.count(); i++) {
cblu's avatar
cblu committed
198
            [attributesArray addObject:child.m_params[i].getNSString()];
darin's avatar
darin committed
199 200 201
        }
        
        KWQPluginPart *newPart = new KWQPluginPart;
darin's avatar
darin committed
202
        newPart->setWidget(new QWidget([_bridge viewForPluginWithURL:url.url().getNSString()
cblu's avatar
cblu committed
203
                                                          attributes:attributesArray
darin's avatar
darin committed
204
                                                             baseURL:d->m_doc->baseURL().getNSString()
cblu's avatar
cblu committed
205
                                                            MIMEType:child.m_args.serviceType.getNSString()]));
darin's avatar
darin committed
206 207 208 209
        return newPart;
    } else {
        LOG(Frames, "name %s", child.m_name.ascii());
        HTMLIFrameElementImpl *o = static_cast<HTMLIFrameElementImpl *>(child.m_frame->element());
darin's avatar
darin committed
210 211 212 213 214 215
        WebCoreBridge *childBridge = [_bridge createChildFrameNamed:child.m_name.getNSString()
                                                            withURL:url.url().getNSString()
                                                         renderPart:child.m_frame
                                                    allowsScrolling:o->scrollingMode() != QScrollView::AlwaysOff
                                                        marginWidth:o->getMarginWidth()
                                                       marginHeight:o->getMarginHeight()];
darin's avatar
darin committed
216
        return [childBridge part];
darin's avatar
darin committed
217
    }
rjw's avatar
rjw committed
218
}
darin's avatar
darin committed
219
    
220
void KWQKHTMLPart::setView(KHTMLView *view, bool weOwnIt)
rjw's avatar
rjw committed
221
{
222 223 224
    if (_ownsView) {
        delete d->m_view;
    }
225
    d->m_view = view;
mjs's avatar
mjs committed
226
    part->setWidget(view);
227
    _ownsView = weOwnIt;
rjw's avatar
rjw committed
228 229
}

230
KHTMLView *KWQKHTMLPart::view() const
darin's avatar
darin committed
231 232 233 234
{
    return d->m_view;
}

235
void KWQKHTMLPart::setTitle(const DOMString &title)
236
{
237
    [_bridge setTitle:title.string().getNSString()];
darin's avatar
darin committed
238 239
}

240
void KWQKHTMLPart::setStatusBarText(const QString &status)
mjs's avatar
mjs committed
241
{
242
    [_bridge setStatusText:status.getNSString()];
mjs's avatar
mjs committed
243 244
}

245
void KWQKHTMLPart::scheduleClose()
mjs's avatar
mjs committed
246
{
247
    [[_bridge window] performSelector:@selector(close) withObject:nil afterDelay:0.0];
mjs's avatar
mjs committed
248 249
}

250
void KWQKHTMLPart::unfocusWindow()
mjs's avatar
mjs committed
251
{
252
    [_bridge unfocusWindow];
mjs's avatar
mjs committed
253
}
rjw's avatar
rjw committed
254

255
void KWQKHTMLPart::jumpToSelection()
256
{
darin's avatar
darin committed
257
    // Assumes that selection will only ever be text nodes. This is currently
258
    // true, but will it always be so?
darin's avatar
darin committed
259 260 261
    if (!d->m_selectionStart.isNull()) {
        RenderText *rt = dynamic_cast<RenderText *>(d->m_selectionStart.handle()->renderer());
        if (rt) {
262 263 264 265
            int x = 0, y = 0;
            rt->posOfChar(d->m_startOffset, x, y);
            // The -50 offset is copied from KHTMLPart::findTextNext, which sets the contents position
            // after finding a matched text string.
darin's avatar
darin committed
266
            d->m_view->setContentsPos(x - 50, y - 50);
267 268 269 270
        }
    }
}

271
void KWQKHTMLPart::redirectionTimerStartedOrStopped()
darin's avatar
darin committed
272 273
{
    if (d->m_redirectionTimer.isActive()) {
darin's avatar
darin committed
274 275 276
        [_bridge reportClientRedirectToURL:d->m_redirectURL.getNSString()
                                     delay:d->m_delayRedirect
                                 fireDate:[d->m_redirectionTimer.getNSTimer() fireDate]];
darin's avatar
darin committed
277
    } else {
278
        [_bridge reportClientRedirectCancelled];
darin's avatar
darin committed
279 280 281 282 283
    }
}

static void moveWidgetsAside(RenderObject *object)
{
darin's avatar
darin committed
284 285 286
    // Would use dynamic_cast, but a virtual function call is faster.
    if (object->isWidget()) {
        QWidget *widget = static_cast<RenderWidget *>(object)->widget();
darin's avatar
darin committed
287 288 289 290 291 292 293 294 295 296
        if (widget) {
            widget->move(999999, 0);
        }
    }
    
    for (RenderObject *child = object->firstChild(); child; child = child->nextSibling()) {
        moveWidgetsAside(child);
    }
}

297
void KWQKHTMLPart::layout()
darin's avatar
darin committed
298 299 300
{
    // Since not all widgets will get a print call, it's important to move them away
    // so that they won't linger in an old position left over from a previous print.
301 302 303
    _needsToSetWidgetsAside = true;
}

304
void KWQKHTMLPart::paint(QPainter *p, const QRect &rect)
305 306 307 308 309 310
{
#ifdef DEBUG_DRAWING
    [[NSColor redColor] set];
    [NSBezierPath fillRect:[view()->getView() visibleRect]];
#endif

311
    if (renderer()) {
312 313 314 315 316
        if (_needsToSetWidgetsAside) {
            moveWidgetsAside(renderer());
            _needsToSetWidgetsAside = false;
        }
        renderer()->layer()->paint(p, rect.x(), rect.y(), rect.width(), rect.height());
darin's avatar
darin committed
317 318
    }
}
darin's avatar
darin committed
319

320
DocumentImpl *KWQKHTMLPart::document()
darin's avatar
darin committed
321 322 323 324
{
    return part->xmlDocImpl();
}

325
RenderObject *KWQKHTMLPart::renderer()
darin's avatar
darin committed
326 327 328 329
{
    DocumentImpl *doc = part->xmlDocImpl();
    return doc ? doc->renderer() : 0;
}
330

331
QString KWQKHTMLPart::userAgent() const
332
{
darin's avatar
darin committed
333
    return QString::fromNSString([_bridge userAgentForURL:part->m_url.url().getNSString()]);
334 335
}

336
NSView *KWQKHTMLPart::nextKeyViewInFrame(NodeImpl *node, KWQSelectionDirection direction)
337 338
{
    DocumentImpl *doc = document();
339 340 341
    if (!doc) {
        return nil;
    }
342
    for (;;) {
343 344
        node = direction == KWQSelectingNext
            ? doc->nextFocusNode(node) : doc->previousFocusNode(node);
345 346 347 348
        if (!node) {
            return nil;
        }
        RenderWidget *renderWidget = dynamic_cast<RenderWidget *>(node->renderer());
349 350 351 352
        if (renderWidget) {
            QWidget *widget = renderWidget->widget();
            KHTMLView *childFrameWidget = dynamic_cast<KHTMLView *>(widget);
            if (childFrameWidget) {
353
                NSView *view = childFrameWidget->part()->kwq->nextKeyViewInFrame(0, direction);
354 355 356
                if (view) {
                    return view;
                }
357
            } else if (widget) {
358 359 360 361 362 363 364 365 366
                NSView *view = widget->getView();
                // AppKit won't be able to handle scrolling and making us the first responder
                // well unless we are actually installed in the correct place. KHTML only does
                // that for visible widgets, so we need to do it explicitly here.
                int x, y;
                if (view && renderWidget->absolutePosition(x, y)) {
                    renderWidget->view()->addChild(widget, x, y);
                    return view;
                }
367 368 369 370 371
            }
        }
    }
}

372
NSView *KWQKHTMLPart::nextKeyViewInFrameHierarchy(NodeImpl *node, KWQSelectionDirection direction)
373 374 375 376 377 378 379 380
{
    NSView *next = nextKeyViewInFrame(node, direction);
    if (next) {
        return next;
    }
    
    KHTMLPart *parentPart = part->parentPart();
    if (parentPart) {
381
        next = parentPart->kwq->nextKeyView(parentPart->frame(part)->m_frame->element(), direction);
382 383 384 385 386 387 388 389
        if (next) {
            return next;
        }
    }
    
    return nil;
}

390
NSView *KWQKHTMLPart::nextKeyView(NodeImpl *node, KWQSelectionDirection direction)
391
{
darin's avatar
darin committed
392
    NSView *next = nextKeyViewInFrameHierarchy(node, direction);
393 394 395 396 397
    if (next) {
        return next;
    }

    // Look at views from the top level part up, looking for a next key view that we can use.
darin's avatar
darin committed
398 399 400 401 402
    next = direction == KWQSelectingNext
        ? [_bridge nextKeyViewOutsideWebViews]
        : [_bridge previousKeyViewOutsideWebViews];
    if (next) {
        return next;
403 404 405
    }
    
    // If all else fails, make a loop by starting from 0.
darin's avatar
darin committed
406 407 408
    return nextKeyViewInFrameHierarchy(0, direction);
}

409
NSView *KWQKHTMLPart::nextKeyViewForWidget(QWidget *startingWidget, KWQSelectionDirection direction)
darin's avatar
darin committed
410 411 412
{
    // Use the event filter object to figure out which RenderWidget owns this QWidget and get to the DOM.
    // Then get the next key view in the order determined by the DOM.
413 414 415 416
    NodeImpl *node = nodeForWidget(startingWidget);
    return partForNode(node)->nextKeyView(node, direction);
}

417
WebCoreBridge *KWQKHTMLPart::bridgeForWidget(QWidget *widget)
418 419 420 421
{
    return partForNode(nodeForWidget(widget))->bridge();
}

422
KWQKHTMLPart *KWQKHTMLPart::partForNode(NodeImpl *node)
423
{
424
    return node->getDocument()->view()->part()->kwq;
425 426
}

427
NodeImpl *KWQKHTMLPart::nodeForWidget(QWidget *widget)
428 429
{
    return static_cast<const RenderWidget *>(widget->eventFilterObject())->element();
430
}
431

432
void KWQKHTMLPart::setDocumentFocus(QWidget *widget)
433 434 435 436 437
{
    NodeImpl *node = nodeForWidget(widget);
    node->getDocument()->setFocusNode(node);
}

438
void KWQKHTMLPart::clearDocumentFocus(QWidget *widget)
439 440 441 442
{
    nodeForWidget(widget)->getDocument()->setFocusNode(0);
}

443
void KWQKHTMLPart::saveDocumentState()
444 445 446 447
{
    [_bridge saveDocumentState];
}

448
void KWQKHTMLPart::restoreDocumentState()
449 450
{
    [_bridge restoreDocumentState];
mjs's avatar
mjs committed
451
}
452

453
QPtrList<KWQKHTMLPart> &KWQKHTMLPart::mutableInstances()
454
{
455
    static QPtrList<KWQKHTMLPart> instancesList;
456 457
    return instancesList;
}
458

459
void KWQKHTMLPart::updatePolicyBaseURL()
460 461 462 463 464 465 466 467
{
    if (part->parentPart()) {
        setPolicyBaseURL(part->parentPart()->docImpl()->policyBaseURL());
    } else {
        setPolicyBaseURL(part->m_url.url());
    }
}

468
void KWQKHTMLPart::setPolicyBaseURL(const DOM::DOMString &s)
469
{
470 471 472 473
    // XML documents will cause this to return null.  docImpl() is
    // an HTMLdocument only. -dwh
    if (part->docImpl())
        part->docImpl()->setPolicyBaseURL(s);
474 475 476
    ConstFrameIt end = d->m_frames.end();
    for (ConstFrameIt it = d->m_frames.begin(); it != end; ++it) {
        ReadOnlyPart *subpart = (*it).m_part;
477
        static_cast<KHTMLPart *>(subpart)->kwq->setPolicyBaseURL(s);
478 479
    }
}
480

481
QString KWQKHTMLPart::requestedURLString() const
482
{
darin's avatar
darin committed
483
    return QString::fromNSString([_bridge requestedURL]);
484 485
}

486
void KWQKHTMLPart::forceLayout()
darin's avatar
darin committed
487 488 489 490 491 492 493
{
    KHTMLView *v = d->m_view;
    if (v) {
        v->layout();
        v->unscheduleRelayout();
    }
}
darin's avatar
darin committed
494

495
QString KWQKHTMLPart::referrer() const
darin's avatar
darin committed
496 497 498
{
    return d->m_referrer;
}
cblu's avatar
cblu committed
499

500
void KWQKHTMLPart::runJavaScriptAlert(const QString &message)
darin's avatar
darin committed
501 502 503 504
{
    [[WebCoreViewFactory sharedFactory] runJavaScriptAlertPanelWithMessage:message.getNSString()];
}

505
bool KWQKHTMLPart::runJavaScriptConfirm(const QString &message)
darin's avatar
darin committed
506 507 508
{
    return [[WebCoreViewFactory sharedFactory] runJavaScriptConfirmPanelWithMessage:message.getNSString()];
}
darin's avatar
darin committed
509

510
bool KWQKHTMLPart::runJavaScriptPrompt(const QString &prompt, const QString &defaultValue, QString &result)
darin's avatar
darin committed
511 512 513 514 515 516 517 518
{
    NSString *returnedText;
    bool ok = [[WebCoreViewFactory sharedFactory] runJavaScriptTextInputPanelWithPrompt:prompt.getNSString()
        defaultText:defaultValue.getNSString() returningText:&returnedText];
    if (ok)
        result = QString::fromNSString(returnedText);
    return ok;
}
darin's avatar
darin committed
519 520 521 522

void KWQKHTMLPart::createDummyDocument()
{
    if (d->m_doc) {
523 524 525 526 527 528 529 530 531 532 533 534
        ASSERT(d->m_view);
    } else {
        d->m_doc = DOMImplementationImpl::instance()->createHTMLDocument(d->m_view);
        d->m_doc->ref();
        
        ASSERT(d->m_view == 0);
        KHTMLView *kview = new KHTMLView(part, 0);
        setView(kview, true);
        
        NSView *view = [[KWQDummyView alloc] initWithWindow:[_bridge window]];
        kview->setView(view);
        [view release];
darin's avatar
darin committed
535 536
    }
}
537 538 539 540 541 542 543

void KWQKHTMLPart::setCurrentEvent(NSEvent *event)
{
    [event retain];
    [_currentEvent release];
    _currentEvent = event;
}
darin's avatar
darin committed
544 545 546 547 548

void KWQKHTMLPart::addMetaData(const QString &key, const QString &value)
{
    d->m_job->addMetaData(key, value);
}
mjs's avatar
mjs committed
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565


bool KWQKHTMLPart::keyEvent(NSEvent *event)
{
    ASSERT([event type] == NSKeyDown || [event type] == NSKeyUp);

    const char *characters = [[event characters] lossyCString];
    int ascii = (characters != nil && strlen(characters) == 1) ? characters[0] : 0;


    QKeyEvent qEvent([event type] == NSKeyDown ? QEvent::KeyPress : QEvent::KeyRelease,
		     [event keyCode],
		     ascii,
		     [_bridge stateForEvent:event],
		     QString::fromNSString([event characters]),
		     [event isARepeat]);

566 567 568 569 570 571 572 573
    // Check for cases where we are too early for events -- possible unmatched key up
    // from pressing return in the location bar.
    DocumentImpl *doc = document();
    if (!doc) {
        return false;
    }
    NodeImpl *node = doc->focusNode();
    if (!node) {
mjs's avatar
mjs committed
574 575 576
	return false;
    }

577
    bool result = node->dispatchKeyEvent(&qEvent);
mjs's avatar
mjs committed
578 579 580 581 582 583 584 585 586 587

    // We want to send both a down and a press for the initial key event
    if (![event isARepeat]) {
	QKeyEvent qEvent([event type] == NSKeyDown ? QEvent::KeyPress : QEvent::KeyRelease,
			 [event keyCode],
			 ascii,
			 [_bridge stateForEvent:event],
			 QString::fromNSString([event characters]),
			 true);
	
588
	result = result && node->dispatchKeyEvent(&qEvent);
mjs's avatar
mjs committed
589 590 591 592
    }

    return result;
}