QWebPage::setNetworkAccessManager

Qt的api里关于QWebPage::setNetworkAccessManager的介绍里有这么一段:

Note: It is currently not supported to change the network access manager after the QWebPage has used it. The results of doing this are undefined.

这么说只能在使用QWebPage之前调用setNetworkAccessManager了,下面分析一下原因。

在qtwebkit中所有与网络请求的模块都由qtnetwork完成。通过gdb分析得到如下信息:

在new QWebPage时调用new QWebPagePrivate(this),初始化QWebFrame为mainFrame(0),即此时QWebFrame为空指针。

每次通过QWebPage::mainFrame()函数得到QWebFrame,代码如下:

QWebFrame *QWebPage::mainFrame() const
{
    d->createMainFrame();
    return d->mainFrame;
}

void QWebPagePrivate::createMainFrame()
{
    if (!mainFrame) {
        QWebFrameData frameData(page);
        mainFrame = new QWebFrame(q, &frameData);

        emit q->frameCreated(mainFrame);
    }
}

分析到这里可以推测“
after the QWebPage has used it ”,即new QWebFrame后,不能调用setNetworkAccessManager()函数。

跟踪new QWebFrame可以得到如下调用关系:

QWebFrame::QWebFrame(QWebPage *parent, QWebFrameData *frameData)
    : QObject(parent)
    , d(new QWebFramePrivate)
{
    d->page = parent;
    d->init(this, frameData);

    if (!frameData->url.isEmpty()) {
        WebCore::ResourceRequest request(frameData->url, frameData->referrer);
        d->frame->loader()->load(request, frameData->name, false);
    }
#if ENABLE(ORIENTATION_EVENTS) && ENABLE(DEVICE_ORIENTATION)
    connect(&d->m_orientation, SIGNAL(readingChanged()), this, SLOT(_q_orientationChanged()));
    d->m_orientation.start();
#endif
}

void QWebFramePrivate::init(QWebFrame *qframe, QWebFrameData *frameData)
{
    q = qframe;

    allowsScrolling = frameData->allowsScrolling;
    marginWidth = frameData->marginWidth;
    marginHeight = frameData->marginHeight;
    frame = frameData->frame.get();
    frameLoaderClient = frameData->frameLoaderClient;
    frameLoaderClient->setFrame(qframe, frame);

    frame->init();
}

 inline void Frame::init()
 {
     m_loader.init();
 }

void FrameLoader::init()
{
    // Propagate sandbox attributes to this Frameloader and its descendants.
    // This needs to be done early, so that an initial document gets correct sandbox flags in its SecurityOrigin.
    updateSandboxFlags();

    // this somewhat odd set of steps is needed to give the frame an initial empty document
    m_stateMachine.advanceTo(FrameLoaderStateMachine::CreatingInitialEmptyDocument);
    setPolicyDocumentLoader(m_client->createDocumentLoader(ResourceRequest(KURL(ParsedURLString, "")), SubstituteData()).get());
    setProvisionalDocumentLoader(m_policyDocumentLoader.get());
    setState(FrameStateProvisional);
    m_provisionalDocumentLoader->setResponse(ResourceResponse(KURL(), "text/html", 0, String(), String()));
    m_provisionalDocumentLoader->finishedLoading();
    m_documentLoader->writer()->begin(KURL(), false);
    m_documentLoader->writer()->end();
    m_frame->document()->cancelParsing();
    m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocument);
    m_didCallImplicitClose = true;

    m_networkingContext = m_client->createNetworkingContext();
}

PassRefPtr FrameLoaderClientQt::createNetworkingContext()
{
    QVariant value = m_webFrame->page()->property("_q_MIMESniffingDisabled");
    bool MIMESniffingDisabled = value.isValid() && value.toBool();

    return FrameNetworkingContextQt::create(m_frame, m_webFrame, !MIMESniffingDisabled, m_webFrame->page()->networkAccessManager());
}


分析调用堆栈,以上过程在new QWebFrame时调用且仅调用一次。m_networkingContext保存了指向QNetworkAccessManager类的指针,再看下面的函数:

NetworkingContext* FrameLoader::networkingContext() const
{
    return m_networkingContext.get();
}

 
  

以后此QWebFrame的所有网络请求都通过networkingContext()函数返回的m_networkingContext完成。故new QWebFrame后再调用setNetworkAccessManager()函数会导致未定义行为。

ps:在一项目中发现从无线切换到有线、或从有线切换到无线后qtwebkit在一段时间内无法正常访问网络(ping通过),经过debug发现只需要替换一下旧的NetworkAccessManager指针就行,上面的分析可以使setNetworkAccessManager有效,但需要稍微修改webkit源码。目前在未修改源码的情况下使用其他方法。

 
  

你可能感兴趣的:(qt)