<commit_before>#include <boost/asio/io_service.hpp>
#include "TcpServer.h"

#include <exception>

#include "ProcessMessageClientConnected.h"
#include "ProcessMessageReceivePacket.h"
#include "ProcessMessageSendPacket.h"
#include "ProcessMessageSocketDisconnected.h"
#include "ProcessMessageTerminate.h"

#include "TcpCommunicationProcess.h"
#include "TcpListenerProcess.h"
#include "ProcessMessageAskForCommProcessInfo.h"
#include "ProcessMessageCommProcessInfo.h"

#define FORCE_VS_DBG_OUTPUT
#include <DebugUtil.h>
#include <ToString.h>

TcpServer::TcpServer()
{
	m_isListening = false;
	m_ioService = new boost::asio::io_service();
	m_listenerProcess = NULL;
	m_uniqueBroadcastPacketIdentifier = 0;
}

TcpServer::~TcpServer()
{
	shutdown();
}

void TcpServer::shutdown()
{
	stopListening();

	for( unsigned int i=0; i<m_communicationProcesses.size(); i++ )
	{
		m_communicationProcesses[i]->putMessage( new ProcessMessageTerminate() );
		m_communicationProcesses[i]->stop();
		delete m_communicationProcesses[i];
	}
	m_communicationProcesses.clear();

	delete m_ioService;
	m_ioService = NULL;
}

void TcpServer::startListening( int p_port )
{
	m_isListening = true;
	m_listenerProcess = new TcpListenerProcess( this, p_port, m_ioService );
	m_listenerProcess->start();
}

void TcpServer::stopListening()
{
	m_isListening = false;
	if( m_listenerProcess )
	{
		m_listenerProcess->putMessage( new ProcessMessageTerminate() );
		m_listenerProcess->stop();
		delete m_listenerProcess;
		m_listenerProcess = NULL;
	}
}

bool TcpServer::isListening()
{
	return m_isListening;
}

bool TcpServer::hasNewConnections()
{
	bool newConnect = false;

	if( m_newConnectionProcesses.size() > 0 )
		newConnect = true;

	return newConnect;
}

unsigned int TcpServer::newConnectionsCount()
{
	return m_newConnectionProcesses.size();
}

unsigned int TcpServer::activeConnectionsCount()
{
	return m_communicationProcesses.size();
}

unsigned int TcpServer::newDisconnectionsCount()
{
	return m_newDisconnectionProcesses.size();
}

bool TcpServer::hasNewDisconnections()
{
	bool newDisconnect = false;

	if( m_newDisconnectionProcesses.size() > 0 )
		newDisconnect = true;

	return newDisconnect;
}

int TcpServer::popNewDisconnection()
{
	int id = -1;
	if( m_newDisconnectionProcesses.size() > 0 )
	{
		id = m_newDisconnectionProcesses.front();
		m_newDisconnectionProcesses.pop();
	}

	return id;
}

bool TcpServer::hasNewPackets()
{
	bool newPacket = false;

	if( m_newPackets.size() > 0 )
		newPacket = true;

	return newPacket;
}

unsigned int TcpServer::newPacketsCount()
{
	return m_newPackets.size();
}

Packet TcpServer::popNewPacket()
{

	if ( !m_newPackets.empty() )
	{
		Packet packet = m_newPackets.front();
		m_newPackets.pop();
		return packet;
	}
	else
	{
		throw domain_error( "Trying to pop from an empty packet queue!" );
	}
	return NULL;
}

void TcpServer::processMessages()
{
	queue< ProcessMessage* > messages;
	messages = checkoutMessageQueue();

	while( messages.size() > 0 )
	{
		ProcessMessage* message = messages.front();
		messages.pop();

		if( message->type == MessageType::CLIENT_CONNECTED )
		{
			ProcessMessageClientConnected* messageClientConnected
				= static_cast<ProcessMessageClientConnected*>(message);

			m_communicationProcesses.push_back(new TcpCommunicationProcess(
				this, messageClientConnected->socket, m_ioService));
			m_communicationProcesses.back()->start();
			m_totalSentInCommProcesses.push_back(0);

			m_newConnectionProcesses.push( m_communicationProcesses.back()->getId() );
		}
		else if( message->type == MessageType::SOCKET_DISCONNECTED )
		{
			ProcessMessageSocketDisconnected* messageSocketDisconnected =
				static_cast< ProcessMessageSocketDisconnected* >(message);

			int processToBeDeleted = -1;
			for( unsigned int i=0; i<m_communicationProcesses.size(); i++ )
			{
				if( messageSocketDisconnected->processId ==
					m_communicationProcesses[i]->getId() )
				{
					m_newDisconnectionProcesses.push(
						m_communicationProcesses[i]->getId() );

					processToBeDeleted = i;
					break;
				}
			}

			if (processToBeDeleted != -1)
			{
				DEBUGPRINT(( (toString("Server terminated comprocess ")
					+ toString(m_communicationProcesses[processToBeDeleted]->getId())
					+ toString("\n")).c_str() ));
				m_communicationProcesses[processToBeDeleted]->putMessage( new ProcessMessageTerminate() );
				m_communicationProcesses[processToBeDeleted]->stop();
				delete m_communicationProcesses[processToBeDeleted];
				m_communicationProcesses.erase(m_communicationProcesses.begin() + processToBeDeleted);
			}
			else
				throw "Something is really knaaas";
		}
		else if( message->type == MessageType::RECEIVE_PACKET )
		{
			m_newPackets.push(
				static_cast< ProcessMessageReceivePacket* >(message)->packet );
		}
		else if( message->type == MessageType::COMM_PROCESS_INFO )
		{
			ProcessMessageCommProcessInfo* commInfoMessage =
				static_cast<ProcessMessageCommProcessInfo*>( message );
			for(unsigned int i=0; i<m_communicationProcesses.size(); i++) {
				if(m_communicationProcesses[i] == commInfoMessage->sender ) {
					m_totalSentInCommProcesses[i] = commInfoMessage->totalPacketsSent;
				}
			}
		}

		delete message;
	}
}

void TcpServer::broadcastPacket( Packet& p_packet, int p_excludeClientId)
{
	giveBroadcastPacketAUniqueIdentifier( &p_packet );
	for( unsigned int i=0; i<m_communicationProcesses.size(); i++ )
	{
		if (m_communicationProcesses[i]->getId()!=p_excludeClientId)
		{
			m_communicationProcesses[i]->putMessage(
				new ProcessMessageSendPacket( this, p_packet ) );
		}
	}
}

void TcpServer::multicastPacket( vector<int> p_connectionIdentities, Packet& p_packet )
{
	for( unsigned int i=0; i<p_connectionIdentities.size(); i++ )
	{
		unicastPacket( p_packet, p_connectionIdentities[i] );
	}
}

void TcpServer::unicastPacket( Packet& p_packet, int p_clientId )
{
	// NOTE: this might be slow enough to do for individual packets
	for ( unsigned int i = 0; i < m_communicationProcesses.size(); i++ )
	{
		if ( m_communicationProcesses[i]->getId() == p_clientId )
		{
			m_communicationProcesses[i]->putMessage(
				new ProcessMessageSendPacket( this, p_packet ) );

			break;
		}
	}
}

void TcpServer::unicastPacketQueue( queue<Packet> p_packets, int p_clientId )
{
	for ( unsigned int i = 0; i < m_communicationProcesses.size(); i++ )
	{
		if ( m_communicationProcesses[i]->getId() == p_clientId )
		{
			queue<ProcessMessage*> messages;
			while( !p_packets.empty() )
			{
				Packet packet = p_packets.front();
				p_packets.pop();
				messages.push( new ProcessMessageSendPacket( this, packet ) );
			}
			m_communicationProcesses[i]->putMessages( messages );

			break;
		}
	}
}

int TcpServer::popNewConnection()
{
	int id = -1;
	if( m_newConnectionProcesses.size() > 0 )
	{
		id = m_newConnectionProcesses.front();
		m_newConnectionProcesses.pop();
	}

	return id;
}

vector< int > TcpServer::getActiveConnections()
{
	vector< int > currentConnections;

	for( unsigned int i=0; i<m_communicationProcesses.size(); i++ )
	{
		currentConnections.push_back( m_communicationProcesses[i]->getId() );
	}

	return currentConnections;
}

void TcpServer::giveBroadcastPacketAUniqueIdentifier( Packet* p_packet )
{
	p_packet->setUniquePacketIdentifier( m_uniqueBroadcastPacketIdentifier );
	m_uniqueBroadcastPacketIdentifier += 1;
}

const unsigned int& TcpServer::getTotalBroadcasts()
{
	return m_uniqueBroadcastPacketIdentifier;
}

void TcpServer::askForCommProcessInfo()
{
	for(unsigned int i=0; i<m_communicationProcesses.size(); i++) {
		m_communicationProcesses[i]->putMessage(
			new ProcessMessageAskForCommProcessInfo( this ) );
	}
}

const unsigned int& TcpServer::totalSentInCommProcess(
	const unsigned int& p_processIdentity )
{
	return m_totalSentInCommProcesses[p_processIdentity];
}

<commit_msg>Poo on you<commit_after>#include <boost/asio/io_service.hpp>
#include "TcpServer.h"

#include <exception>

#include "ProcessMessageClientConnected.h"
#include "ProcessMessageReceivePacket.h"
#include "ProcessMessageSendPacket.h"
#include "ProcessMessageSocketDisconnected.h"
#include "ProcessMessageTerminate.h"

#include "TcpCommunicationProcess.h"
#include "TcpListenerProcess.h"
#include "ProcessMessageAskForCommProcessInfo.h"
#include "ProcessMessageCommProcessInfo.h"

#define FORCE_VS_DBG_OUTPUT
#include <DebugUtil.h>
#include <ToString.h>

TcpServer::TcpServer()
{
	m_isListening = false;
	m_ioService = new boost::asio::io_service();
	m_listenerProcess = NULL;
	m_uniqueBroadcastPacketIdentifier = 0;
}

TcpServer::~TcpServer()
{
	shutdown();
}

void TcpServer::shutdown()
{
	stopListening();

	for( unsigned int i=0; i<m_communicationProcesses.size(); i++ )
	{
		m_communicationProcesses[i]->putMessage( new ProcessMessageTerminate() );
		m_communicationProcesses[i]->stop();
		delete m_communicationProcesses[i];
	}
	m_communicationProcesses.clear();

	delete m_ioService;
	m_ioService = NULL;
}

void TcpServer::startListening( int p_port )
{
	m_isListening = true;
	m_listenerProcess = new TcpListenerProcess( this, p_port, m_ioService );
	m_listenerProcess->start();
}

void TcpServer::stopListening()
{
	m_isListening = false;
	if( m_listenerProcess )
	{
		m_listenerProcess->putMessage( new ProcessMessageTerminate() );
		m_listenerProcess->stop();
		delete m_listenerProcess;
		m_listenerProcess = NULL;
	}

}

bool TcpServer::isListening()
{
	return m_isListening;
}

bool TcpServer::hasNewConnections()
{
	bool newConnect = false;

	if( m_newConnectionProcesses.size() > 0 )
		newConnect = true;

	return newConnect;
}

unsigned int TcpServer::newConnectionsCount()
{
	return m_newConnectionProcesses.size();
}

unsigned int TcpServer::activeConnectionsCount()
{
	return m_communicationProcesses.size();
}

unsigned int TcpServer::newDisconnectionsCount()
{
	return m_newDisconnectionProcesses.size();
}

bool TcpServer::hasNewDisconnections()
{
	bool newDisconnect = false;

	if( m_newDisconnectionProcesses.size() > 0 )
		newDisconnect = true;

	return newDisconnect;
}

int TcpServer::popNewDisconnection()
{
	int id = -1;
	if( m_newDisconnectionProcesses.size() > 0 )
	{
		id = m_newDisconnectionProcesses.front();
		m_newDisconnectionProcesses.pop();
	}

	return id;
}

bool TcpServer::hasNewPackets()
{
	bool newPacket = false;

	if( m_newPackets.size() > 0 )
		newPacket = true;

	return newPacket;
}

unsigned int TcpServer::newPacketsCount()
{
	return m_newPackets.size();
}

Packet TcpServer::popNewPacket()
{

	if ( !m_newPackets.empty() )
	{
		Packet packet = m_newPackets.front();
		m_newPackets.pop();
		return packet;
	}
	else
	{
		throw domain_error( "Trying to pop from an empty packet queue!" );
	}
	return NULL;
}

void TcpServer::processMessages()
{
	queue< ProcessMessage* > messages;
	messages = checkoutMessageQueue();

	while( messages.size() > 0 )
	{
		ProcessMessage* message = messages.front();
		messages.pop();

		if( message->type == MessageType::CLIENT_CONNECTED )
		{
			ProcessMessageClientConnected* messageClientConnected
				= static_cast<ProcessMessageClientConnected*>(message);

			m_communicationProcesses.push_back(new TcpCommunicationProcess(
				this, messageClientConnected->socket, m_ioService));
			m_communicationProcesses.back()->start();
			m_totalSentInCommProcesses.push_back(0);

			m_newConnectionProcesses.push( m_communicationProcesses.back()->getId() );
		}
		else if( message->type == MessageType::SOCKET_DISCONNECTED )
		{
			ProcessMessageSocketDisconnected* messageSocketDisconnected =
				static_cast< ProcessMessageSocketDisconnected* >(message);

			int processToBeDeleted = -1;
			for( unsigned int i=0; i<m_communicationProcesses.size(); i++ )
			{
				if( messageSocketDisconnected->processId ==
					m_communicationProcesses[i]->getId() )
				{
					m_newDisconnectionProcesses.push(
						m_communicationProcesses[i]->getId() );

					processToBeDeleted = i;
					break;
				}
			}

			if (processToBeDeleted != -1)
			{
				DEBUGPRINT(( (toString("Server terminated comprocess ")
					+ toString(m_communicationProcesses[processToBeDeleted]->getId())
					+ toString("\n")).c_str() ));
				m_communicationProcesses[processToBeDeleted]->putMessage( new ProcessMessageTerminate() );
				m_communicationProcesses[processToBeDeleted]->stop();
				delete m_communicationProcesses[processToBeDeleted];
				m_communicationProcesses.erase(m_communicationProcesses.begin() + processToBeDeleted);
			}
			else
				throw "Something is really knaaas";
		}
		else if( message->type == MessageType::RECEIVE_PACKET )
		{
			m_newPackets.push(
				static_cast< ProcessMessageReceivePacket* >(message)->packet );
		}
		else if( message->type == MessageType::COMM_PROCESS_INFO )
		{
			ProcessMessageCommProcessInfo* commInfoMessage =
				static_cast<ProcessMessageCommProcessInfo*>( message );
			for(unsigned int i=0; i<m_communicationProcesses.size(); i++) {
				if(m_communicationProcesses[i] == commInfoMessage->sender ) {
					m_totalSentInCommProcesses[i] = commInfoMessage->totalPacketsSent;
				}
			}
		}

		delete message;
	}
}

void TcpServer::broadcastPacket( Packet& p_packet, int p_excludeClientId)
{
	giveBroadcastPacketAUniqueIdentifier( &p_packet );
	for( unsigned int i=0; i<m_communicationProcesses.size(); i++ )
	{
		if (m_communicationProcesses[i]->getId()!=p_excludeClientId)
		{
			m_communicationProcesses[i]->putMessage(
				new ProcessMessageSendPacket( this, p_packet ) );
		}
	}
}

void TcpServer::multicastPacket( vector<int> p_connectionIdentities, Packet& p_packet )
{
	for( unsigned int i=0; i<p_connectionIdentities.size(); i++ )
	{
		unicastPacket( p_packet, p_connectionIdentities[i] );
	}
}

void TcpServer::unicastPacket( Packet& p_packet, int p_clientId )
{
	// NOTE: this might be slow enough to do for individual packets
	for ( unsigned int i = 0; i < m_communicationProcesses.size(); i++ )
	{
		if ( m_communicationProcesses[i]->getId() == p_clientId )
		{
			m_communicationProcesses[i]->putMessage(
				new ProcessMessageSendPacket( this, p_packet ) );

			break;
		}
	}
}

void TcpServer::unicastPacketQueue( queue<Packet> p_packets, int p_clientId )
{
	for ( unsigned int i = 0; i < m_communicationProcesses.size(); i++ )
	{
		if ( m_communicationProcesses[i]->getId() == p_clientId )
		{
			queue<ProcessMessage*> messages;
			while( !p_packets.empty() )
			{
				Packet packet = p_packets.front();
				p_packets.pop();
				messages.push( new ProcessMessageSendPacket( this, packet ) );
			}
			m_communicationProcesses[i]->putMessages( messages );

			break;
		}
	}
}

int TcpServer::popNewConnection()
{
	int id = -1;
	if( m_newConnectionProcesses.size() > 0 )
	{
		id = m_newConnectionProcesses.front();
		m_newConnectionProcesses.pop();
	}

	return id;
}

vector< int > TcpServer::getActiveConnections()
{
	vector< int > currentConnections;

	for( unsigned int i=0; i<m_communicationProcesses.size(); i++ )
	{
		currentConnections.push_back( m_communicationProcesses[i]->getId() );
	}

	return currentConnections;
}

void TcpServer::giveBroadcastPacketAUniqueIdentifier( Packet* p_packet )
{
	p_packet->setUniquePacketIdentifier( m_uniqueBroadcastPacketIdentifier );
	m_uniqueBroadcastPacketIdentifier += 1;
}

const unsigned int& TcpServer::getTotalBroadcasts()
{
	return m_uniqueBroadcastPacketIdentifier;
}

void TcpServer::askForCommProcessInfo()
{
	for(unsigned int i=0; i<m_communicationProcesses.size(); i++) {
		m_communicationProcesses[i]->putMessage(
			new ProcessMessageAskForCommProcessInfo( this ) );
	}
}

const unsigned int& TcpServer::totalSentInCommProcess(
	const unsigned int& p_processIdentity )
{
	return m_totalSentInCommProcesses[p_processIdentity];
}

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2011 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * 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.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

#include "config.h"
#include "core/inspector/PageRuntimeAgent.h"

#include "bindings/v8/DOMWrapperWorld.h"
#include "bindings/v8/ScriptController.h"
#include "core/inspector/InjectedScript.h"
#include "core/inspector/InjectedScriptManager.h"
#include "core/inspector/InspectorPageAgent.h"
#include "core/inspector/InspectorState.h"
#include "core/inspector/InstrumentingAgents.h"
#include "core/page/Frame.h"
#include "core/page/Page.h"
#include "core/page/PageConsole.h"
#include "weborigin/SecurityOrigin.h"

using WebCore::TypeBuilder::Runtime::ExecutionContextDescription;

namespace WebCore {

namespace PageRuntimeAgentState {
static const char runtimeEnabled[] = "runtimeEnabled";
};

PageRuntimeAgent::PageRuntimeAgent(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* state, InjectedScriptManager* injectedScriptManager, ScriptDebugServer* scriptDebugServer, Page* page, InspectorPageAgent* pageAgent)
    : InspectorRuntimeAgent(instrumentingAgents, state, injectedScriptManager, scriptDebugServer)
    , m_inspectedPage(page)
    , m_pageAgent(pageAgent)
    , m_frontend(0)
    , m_mainWorldContextCreated(false)
{
    m_instrumentingAgents->setPageRuntimeAgent(this);
}

PageRuntimeAgent::~PageRuntimeAgent()
{
    m_instrumentingAgents->setPageRuntimeAgent(0);
}

void PageRuntimeAgent::setFrontend(InspectorFrontend* frontend)
{
    m_frontend = frontend->runtime();
}

void PageRuntimeAgent::clearFrontend()
{
    m_frontend = 0;
    String errorString;
    disable(&errorString);
}

void PageRuntimeAgent::restore()
{
    if (m_state->getBoolean(PageRuntimeAgentState::runtimeEnabled)) {
        String error;
        enable(&error);
    }
}

void PageRuntimeAgent::enable(ErrorString* errorString)
{
    if (m_enabled)
        return;

    InspectorRuntimeAgent::enable(errorString);
    m_state->setBoolean(PageRuntimeAgentState::runtimeEnabled, true);
    // Only report existing contexts if the page did commit load, otherwise we may
    // unintentionally initialize contexts in the frames which may trigger some listeners
    // that are expected to be triggered only after the load is committed, see http://crbug.com/131623
    if (m_mainWorldContextCreated)
        reportExecutionContextCreation();
}

void PageRuntimeAgent::disable(ErrorString* errorString)
{
    if (!m_enabled)
        return;

    InspectorRuntimeAgent::disable(errorString);
    m_state->setBoolean(PageRuntimeAgentState::runtimeEnabled, false);
}

void PageRuntimeAgent::didClearWindowObjectInWorld(Frame* frame, DOMWrapperWorld* world)
{
    if (world != mainThreadNormalWorld())
        return;

    m_mainWorldContextCreated = true;

    if (!m_enabled)
        return;
    ASSERT(m_frontend);
    String frameId = m_pageAgent->frameId(frame);
    ScriptState* scriptState = mainWorldScriptState(frame);
    notifyContextCreated(frameId, scriptState, 0, true);
}

void PageRuntimeAgent::didCreateIsolatedContext(Frame* frame, ScriptState* scriptState, SecurityOrigin* origin)
{
    if (!m_enabled)
        return;
    ASSERT(m_frontend);
    String frameId = m_pageAgent->frameId(frame);
    notifyContextCreated(frameId, scriptState, origin, false);
}

InjectedScript PageRuntimeAgent::injectedScriptForEval(ErrorString* errorString, const int* executionContextId)
{
    if (!executionContextId) {
        // We shouldn't be able to create an injected script if the main
        // window shell not initialized. See crbug.com/263162.
        ScriptController* scriptController = m_inspectedPage->mainFrame()->script();
        if (!scriptController->existingWindowShell(mainThreadNormalWorld())) {
            *errorString = "Window shell for main world not initialized yet.";
            return InjectedScript();
        }

        ScriptState* scriptState = mainWorldScriptState(m_inspectedPage->mainFrame());
        InjectedScript result = injectedScriptManager()->injectedScriptFor(scriptState);
        if (result.hasNoValue())
            *errorString = "Internal error: main world execution context not found.";
        return result;
    }
    InjectedScript injectedScript = injectedScriptManager()->injectedScriptForId(*executionContextId);
    if (injectedScript.hasNoValue())
        *errorString = "Execution context with given id not found.";
    return injectedScript;
}

void PageRuntimeAgent::muteConsole()
{
    PageConsole::mute();
}

void PageRuntimeAgent::unmuteConsole()
{
    PageConsole::unmute();
}

void PageRuntimeAgent::reportExecutionContextCreation()
{
    Vector<std::pair<ScriptState*, SecurityOrigin*> > isolatedContexts;
    for (Frame* frame = m_inspectedPage->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
        if (!frame->script()->canExecuteScripts(NotAboutToExecuteScript))
            continue;
        String frameId = m_pageAgent->frameId(frame);

        ScriptState* scriptState = mainWorldScriptState(frame);
        notifyContextCreated(frameId, scriptState, 0, true);
        frame->script()->collectIsolatedContexts(isolatedContexts);
        if (isolatedContexts.isEmpty())
            continue;
        for (size_t i = 0; i< isolatedContexts.size(); i++)
            notifyContextCreated(frameId, isolatedContexts[i].first, isolatedContexts[i].second, false);
        isolatedContexts.clear();
    }
}

void PageRuntimeAgent::notifyContextCreated(const String& frameId, ScriptState* scriptState, SecurityOrigin* securityOrigin, bool isPageContext)
{
    ASSERT(securityOrigin || isPageContext);
    int executionContextId = injectedScriptManager()->injectedScriptIdFor(scriptState);
    String name = securityOrigin ? securityOrigin->toRawString() : "";
    m_frontend->executionContextCreated(ExecutionContextDescription::create()
        .setId(executionContextId)
        .setIsPageContext(isPageContext)
        .setName(name)
        .setFrameId(frameId)
        .release());
}

} // namespace WebCore

<commit_msg>Revert 158684 "Fix creating InjectedScript too early causing ext..."<commit_after>/*
 * Copyright (C) 2011 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * 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.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

#include "config.h"
#include "core/inspector/PageRuntimeAgent.h"

#include "bindings/v8/DOMWrapperWorld.h"
#include "bindings/v8/ScriptController.h"
#include "core/inspector/InjectedScript.h"
#include "core/inspector/InjectedScriptManager.h"
#include "core/inspector/InspectorPageAgent.h"
#include "core/inspector/InspectorState.h"
#include "core/inspector/InstrumentingAgents.h"
#include "core/page/Frame.h"
#include "core/page/Page.h"
#include "core/page/PageConsole.h"
#include "weborigin/SecurityOrigin.h"

using WebCore::TypeBuilder::Runtime::ExecutionContextDescription;

namespace WebCore {

namespace PageRuntimeAgentState {
static const char runtimeEnabled[] = "runtimeEnabled";
};

PageRuntimeAgent::PageRuntimeAgent(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* state, InjectedScriptManager* injectedScriptManager, ScriptDebugServer* scriptDebugServer, Page* page, InspectorPageAgent* pageAgent)
    : InspectorRuntimeAgent(instrumentingAgents, state, injectedScriptManager, scriptDebugServer)
    , m_inspectedPage(page)
    , m_pageAgent(pageAgent)
    , m_frontend(0)
    , m_mainWorldContextCreated(false)
{
    m_instrumentingAgents->setPageRuntimeAgent(this);
}

PageRuntimeAgent::~PageRuntimeAgent()
{
    m_instrumentingAgents->setPageRuntimeAgent(0);
}

void PageRuntimeAgent::setFrontend(InspectorFrontend* frontend)
{
    m_frontend = frontend->runtime();
}

void PageRuntimeAgent::clearFrontend()
{
    m_frontend = 0;
    String errorString;
    disable(&errorString);
}

void PageRuntimeAgent::restore()
{
    if (m_state->getBoolean(PageRuntimeAgentState::runtimeEnabled)) {
        String error;
        enable(&error);
    }
}

void PageRuntimeAgent::enable(ErrorString* errorString)
{
    if (m_enabled)
        return;

    InspectorRuntimeAgent::enable(errorString);
    m_state->setBoolean(PageRuntimeAgentState::runtimeEnabled, true);
    // Only report existing contexts if the page did commit load, otherwise we may
    // unintentionally initialize contexts in the frames which may trigger some listeners
    // that are expected to be triggered only after the load is committed, see http://crbug.com/131623
    if (m_mainWorldContextCreated)
        reportExecutionContextCreation();
}

void PageRuntimeAgent::disable(ErrorString* errorString)
{
    if (!m_enabled)
        return;

    InspectorRuntimeAgent::disable(errorString);
    m_state->setBoolean(PageRuntimeAgentState::runtimeEnabled, false);
}

void PageRuntimeAgent::didClearWindowObjectInWorld(Frame* frame, DOMWrapperWorld* world)
{
    if (world != mainThreadNormalWorld())
        return;

    m_mainWorldContextCreated = true;

    if (!m_enabled)
        return;
    ASSERT(m_frontend);
    String frameId = m_pageAgent->frameId(frame);
    ScriptState* scriptState = mainWorldScriptState(frame);
    notifyContextCreated(frameId, scriptState, 0, true);
}

void PageRuntimeAgent::didCreateIsolatedContext(Frame* frame, ScriptState* scriptState, SecurityOrigin* origin)
{
    if (!m_enabled)
        return;
    ASSERT(m_frontend);
    String frameId = m_pageAgent->frameId(frame);
    notifyContextCreated(frameId, scriptState, origin, false);
}

InjectedScript PageRuntimeAgent::injectedScriptForEval(ErrorString* errorString, const int* executionContextId)
{
    if (!executionContextId) {
        ScriptState* scriptState = mainWorldScriptState(m_inspectedPage->mainFrame());
        InjectedScript result = injectedScriptManager()->injectedScriptFor(scriptState);
        if (result.hasNoValue())
            *errorString = "Internal error: main world execution context not found.";
        return result;
    }
    InjectedScript injectedScript = injectedScriptManager()->injectedScriptForId(*executionContextId);
    if (injectedScript.hasNoValue())
        *errorString = "Execution context with given id not found.";
    return injectedScript;
}

void PageRuntimeAgent::muteConsole()
{
    PageConsole::mute();
}

void PageRuntimeAgent::unmuteConsole()
{
    PageConsole::unmute();
}

void PageRuntimeAgent::reportExecutionContextCreation()
{
    Vector<std::pair<ScriptState*, SecurityOrigin*> > isolatedContexts;
    for (Frame* frame = m_inspectedPage->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
        if (!frame->script()->canExecuteScripts(NotAboutToExecuteScript))
            continue;
        String frameId = m_pageAgent->frameId(frame);

        ScriptState* scriptState = mainWorldScriptState(frame);
        notifyContextCreated(frameId, scriptState, 0, true);
        frame->script()->collectIsolatedContexts(isolatedContexts);
        if (isolatedContexts.isEmpty())
            continue;
        for (size_t i = 0; i< isolatedContexts.size(); i++)
            notifyContextCreated(frameId, isolatedContexts[i].first, isolatedContexts[i].second, false);
        isolatedContexts.clear();
    }
}

void PageRuntimeAgent::notifyContextCreated(const String& frameId, ScriptState* scriptState, SecurityOrigin* securityOrigin, bool isPageContext)
{
    ASSERT(securityOrigin || isPageContext);
    int executionContextId = injectedScriptManager()->injectedScriptIdFor(scriptState);
    String name = securityOrigin ? securityOrigin->toRawString() : "";
    m_frontend->executionContextCreated(ExecutionContextDescription::create()
        .setId(executionContextId)
        .setIsPageContext(isPageContext)
        .setName(name)
        .setFrameId(frameId)
        .release());
}

} // namespace WebCore

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2011 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * 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.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

#include "config.h"
#include "core/inspector/PageRuntimeAgent.h"

#include "bindings/v8/DOMWrapperWorld.h"
#include "bindings/v8/ScriptController.h"
#include "core/inspector/InjectedScript.h"
#include "core/inspector/InjectedScriptManager.h"
#include "core/inspector/InspectorPageAgent.h"
#include "core/inspector/InspectorState.h"
#include "core/inspector/InstrumentingAgents.h"
#include "core/page/Frame.h"
#include "core/page/Page.h"
#include "core/page/PageConsole.h"
#include "weborigin/SecurityOrigin.h"

using WebCore::TypeBuilder::Runtime::ExecutionContextDescription;

namespace WebCore {

namespace PageRuntimeAgentState {
static const char runtimeEnabled[] = "runtimeEnabled";
};

PageRuntimeAgent::PageRuntimeAgent(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* state, InjectedScriptManager* injectedScriptManager, ScriptDebugServer* scriptDebugServer, Page* page, InspectorPageAgent* pageAgent)
    : InspectorRuntimeAgent(instrumentingAgents, state, injectedScriptManager, scriptDebugServer)
    , m_inspectedPage(page)
    , m_pageAgent(pageAgent)
    , m_frontend(0)
    , m_mainWorldContextCreated(false)
{
    m_instrumentingAgents->setPageRuntimeAgent(this);
}

PageRuntimeAgent::~PageRuntimeAgent()
{
    m_instrumentingAgents->setPageRuntimeAgent(0);
}

void PageRuntimeAgent::setFrontend(InspectorFrontend* frontend)
{
    m_frontend = frontend->runtime();
}

void PageRuntimeAgent::clearFrontend()
{
    m_frontend = 0;
    String errorString;
    disable(&errorString);
}

void PageRuntimeAgent::restore()
{
    if (m_state->getBoolean(PageRuntimeAgentState::runtimeEnabled)) {
        String error;
        enable(&error);
    }
}

void PageRuntimeAgent::enable(ErrorString* errorString)
{
    if (m_enabled)
        return;

    InspectorRuntimeAgent::enable(errorString);
    m_state->setBoolean(PageRuntimeAgentState::runtimeEnabled, true);
    // Only report existing contexts if the page did commit load, otherwise we may
    // unintentionally initialize contexts in the frames which may trigger some listeners
    // that are expected to be triggered only after the load is committed, see http://crbug.com/131623
    if (m_mainWorldContextCreated)
        reportExecutionContextCreation();
}

void PageRuntimeAgent::disable(ErrorString* errorString)
{
    if (!m_enabled)
        return;

    InspectorRuntimeAgent::disable(errorString);
    m_state->setBoolean(PageRuntimeAgentState::runtimeEnabled, false);
}

void PageRuntimeAgent::didClearWindowObjectInWorld(Frame* frame, DOMWrapperWorld* world)
{
    if (world != mainThreadNormalWorld())
        return;

    m_mainWorldContextCreated = true;

    if (!m_enabled)
        return;
    ASSERT(m_frontend);
    String frameId = m_pageAgent->frameId(frame);
    ScriptState* scriptState = mainWorldScriptState(frame);
    notifyContextCreated(frameId, scriptState, 0, true);
}

void PageRuntimeAgent::didCreateIsolatedContext(Frame* frame, ScriptState* scriptState, SecurityOrigin* origin)
{
    if (!m_enabled)
        return;
    ASSERT(m_frontend);
    String frameId = m_pageAgent->frameId(frame);
    notifyContextCreated(frameId, scriptState, origin, false);
}

InjectedScript PageRuntimeAgent::injectedScriptForEval(ErrorString* errorString, const int* executionContextId)
{
    if (!executionContextId) {
        ScriptState* scriptState = mainWorldScriptState(m_inspectedPage->mainFrame());
        InjectedScript result = injectedScriptManager()->injectedScriptFor(scriptState);
        if (result.hasNoValue())
            *errorString = "Internal error: main world execution context not found.";
        return result;
    }
    InjectedScript injectedScript = injectedScriptManager()->injectedScriptForId(*executionContextId);
    if (injectedScript.hasNoValue())
        *errorString = "Execution context with given id not found.";
    return injectedScript;
}

void PageRuntimeAgent::muteConsole()
{
    PageConsole::mute();
}

void PageRuntimeAgent::unmuteConsole()
{
    PageConsole::unmute();
}

void PageRuntimeAgent::reportExecutionContextCreation()
{
    Vector<std::pair<ScriptState*, SecurityOrigin*> > isolatedContexts;
    for (Frame* frame = m_inspectedPage->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
        if (!frame->script()->canExecuteScripts(NotAboutToExecuteScript))
            continue;
        String frameId = m_pageAgent->frameId(frame);

        ScriptState* scriptState = mainWorldScriptState(frame);
        notifyContextCreated(frameId, scriptState, 0, true);
        frame->script()->collectIsolatedContexts(isolatedContexts);
        if (isolatedContexts.isEmpty())
            continue;
        for (size_t i = 0; i< isolatedContexts.size(); i++)
            notifyContextCreated(frameId, isolatedContexts[i].first, isolatedContexts[i].second, false);
        isolatedContexts.clear();
    }
}

void PageRuntimeAgent::notifyContextCreated(const String& frameId, ScriptState* scriptState, SecurityOrigin* securityOrigin, bool isPageContext)
{
    ASSERT(securityOrigin || isPageContext);
    int executionContextId = injectedScriptManager()->injectedScriptIdFor(scriptState);
    String name = securityOrigin ? securityOrigin->toRawString() : "";
    m_frontend->executionContextCreated(ExecutionContextDescription::create()
        .setId(executionContextId)
        .setIsPageContext(isPageContext)
        .setName(name)
        .setFrameId(frameId)
        .release());
}

} // namespace WebCore

<commit_msg>Fix creating InjectedScript too early causing extension bindings not to work.<commit_after>/*
 * Copyright (C) 2011 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * 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.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

#include "config.h"
#include "core/inspector/PageRuntimeAgent.h"

#include "bindings/v8/DOMWrapperWorld.h"
#include "bindings/v8/ScriptController.h"
#include "core/inspector/InjectedScript.h"
#include "core/inspector/InjectedScriptManager.h"
#include "core/inspector/InspectorPageAgent.h"
#include "core/inspector/InspectorState.h"
#include "core/inspector/InstrumentingAgents.h"
#include "core/page/Frame.h"
#include "core/page/Page.h"
#include "core/page/PageConsole.h"
#include "weborigin/SecurityOrigin.h"

using WebCore::TypeBuilder::Runtime::ExecutionContextDescription;

namespace WebCore {

namespace PageRuntimeAgentState {
static const char runtimeEnabled[] = "runtimeEnabled";
};

PageRuntimeAgent::PageRuntimeAgent(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* state, InjectedScriptManager* injectedScriptManager, ScriptDebugServer* scriptDebugServer, Page* page, InspectorPageAgent* pageAgent)
    : InspectorRuntimeAgent(instrumentingAgents, state, injectedScriptManager, scriptDebugServer)
    , m_inspectedPage(page)
    , m_pageAgent(pageAgent)
    , m_frontend(0)
    , m_mainWorldContextCreated(false)
{
    m_instrumentingAgents->setPageRuntimeAgent(this);
}

PageRuntimeAgent::~PageRuntimeAgent()
{
    m_instrumentingAgents->setPageRuntimeAgent(0);
}

void PageRuntimeAgent::setFrontend(InspectorFrontend* frontend)
{
    m_frontend = frontend->runtime();
}

void PageRuntimeAgent::clearFrontend()
{
    m_frontend = 0;
    String errorString;
    disable(&errorString);
}

void PageRuntimeAgent::restore()
{
    if (m_state->getBoolean(PageRuntimeAgentState::runtimeEnabled)) {
        String error;
        enable(&error);
    }
}

void PageRuntimeAgent::enable(ErrorString* errorString)
{
    if (m_enabled)
        return;

    InspectorRuntimeAgent::enable(errorString);
    m_state->setBoolean(PageRuntimeAgentState::runtimeEnabled, true);
    // Only report existing contexts if the page did commit load, otherwise we may
    // unintentionally initialize contexts in the frames which may trigger some listeners
    // that are expected to be triggered only after the load is committed, see http://crbug.com/131623
    if (m_mainWorldContextCreated)
        reportExecutionContextCreation();
}

void PageRuntimeAgent::disable(ErrorString* errorString)
{
    if (!m_enabled)
        return;

    InspectorRuntimeAgent::disable(errorString);
    m_state->setBoolean(PageRuntimeAgentState::runtimeEnabled, false);
}

void PageRuntimeAgent::didClearWindowObjectInWorld(Frame* frame, DOMWrapperWorld* world)
{
    if (world != mainThreadNormalWorld())
        return;

    m_mainWorldContextCreated = true;

    if (!m_enabled)
        return;
    ASSERT(m_frontend);
    String frameId = m_pageAgent->frameId(frame);
    ScriptState* scriptState = mainWorldScriptState(frame);
    notifyContextCreated(frameId, scriptState, 0, true);
}

void PageRuntimeAgent::didCreateIsolatedContext(Frame* frame, ScriptState* scriptState, SecurityOrigin* origin)
{
    if (!m_enabled)
        return;
    ASSERT(m_frontend);
    String frameId = m_pageAgent->frameId(frame);
    notifyContextCreated(frameId, scriptState, origin, false);
}

InjectedScript PageRuntimeAgent::injectedScriptForEval(ErrorString* errorString, const int* executionContextId)
{
    if (!executionContextId) {
        // We shouldn't be able to create an injected script if the main
        // window shell not initialized. See crbug.com/263162.
        ScriptController* scriptController = m_inspectedPage->mainFrame()->script();
        if (!scriptController->existingWindowShell(mainThreadNormalWorld())) {
            *errorString = "Window shell for main world not initialized yet.";
            return InjectedScript();
        }

        ScriptState* scriptState = mainWorldScriptState(m_inspectedPage->mainFrame());
        InjectedScript result = injectedScriptManager()->injectedScriptFor(scriptState);
        if (result.hasNoValue())
            *errorString = "Internal error: main world execution context not found.";
        return result;
    }
    InjectedScript injectedScript = injectedScriptManager()->injectedScriptForId(*executionContextId);
    if (injectedScript.hasNoValue())
        *errorString = "Execution context with given id not found.";
    return injectedScript;
}

void PageRuntimeAgent::muteConsole()
{
    PageConsole::mute();
}

void PageRuntimeAgent::unmuteConsole()
{
    PageConsole::unmute();
}

void PageRuntimeAgent::reportExecutionContextCreation()
{
    Vector<std::pair<ScriptState*, SecurityOrigin*> > isolatedContexts;
    for (Frame* frame = m_inspectedPage->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
        if (!frame->script()->canExecuteScripts(NotAboutToExecuteScript))
            continue;
        String frameId = m_pageAgent->frameId(frame);

        ScriptState* scriptState = mainWorldScriptState(frame);
        notifyContextCreated(frameId, scriptState, 0, true);
        frame->script()->collectIsolatedContexts(isolatedContexts);
        if (isolatedContexts.isEmpty())
            continue;
        for (size_t i = 0; i< isolatedContexts.size(); i++)
            notifyContextCreated(frameId, isolatedContexts[i].first, isolatedContexts[i].second, false);
        isolatedContexts.clear();
    }
}

void PageRuntimeAgent::notifyContextCreated(const String& frameId, ScriptState* scriptState, SecurityOrigin* securityOrigin, bool isPageContext)
{
    ASSERT(securityOrigin || isPageContext);
    int executionContextId = injectedScriptManager()->injectedScriptIdFor(scriptState);
    String name = securityOrigin ? securityOrigin->toRawString() : "";
    m_frontend->executionContextCreated(ExecutionContextDescription::create()
        .setId(executionContextId)
        .setIsPageContext(isPageContext)
        .setName(name)
        .setFrameId(frameId)
        .release());
}

} // namespace WebCore

<|endoftext|>


<commit_before>/* TyTools - public domain
   Niels Martignène <niels.martignene@protonmail.com>
   https://koromix.dev/tytools

   This software is in the public domain. Where that dedication is not
   recognized, you are granted a perpetual, irrevocable license to copy,
   distribute, and modify this file as you see fit.

   See the LICENSE file for more details. */

#include <QIdentityProxyModel>
#include <QItemDelegate>
#include <QPushButton>

#include "board.hpp"
#include "monitor.hpp"
#include "selector_dialog.hpp"
#include "tycommander.hpp"

using namespace std;

class SelectorDialogModel: public QIdentityProxyModel {
public:
    SelectorDialogModel(QObject *parent = nullptr)
        : QIdentityProxyModel(parent) {}

    int columnCount(const QModelIndex &parent) const override;
    QVariant data(const QModelIndex &index, int role) const override;
};

class SelectorDialogItemDelegate: public QItemDelegate {
public:
    SelectorDialogItemDelegate(QObject *parent = nullptr)
        : QItemDelegate(parent) {}

    QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};

int SelectorDialogModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return 3;
}

QVariant SelectorDialogModel::data(const QModelIndex &index, int role) const
{
    if (index.column() == Monitor::COLUMN_BOARD) {
        switch (role) {
        case Qt::TextAlignmentRole:
            return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
        }
    } else if (index.column() == Monitor::COLUMN_MODEL) {
        switch (role) {
        case Qt::TextAlignmentRole:
            return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
        }
    } else if (index.column() == Monitor::COLUMN_STATUS) {
        switch (role) {
        case Qt::TextAlignmentRole:
            return QVariant(Qt::AlignRight | Qt::AlignVCenter);
        case Qt::ForegroundRole:
            return QBrush(Qt::darkGray);
        }
    }

    return QIdentityProxyModel::data(index, role);

}

QSize SelectorDialogItemDelegate::sizeHint(const QStyleOptionViewItem &option,
                                           const QModelIndex &index) const
{
    auto size = QItemDelegate::sizeHint(option, index);
    size.setHeight(24);
    return size;
}

SelectorDialog::SelectorDialog(QWidget *parent)
    : QDialog(parent), monitor_(tyCommander->monitor())
{
    setupUi(this);

    connect(buttonBox, &QDialogButtonBox::accepted, this, &SelectorDialog::accept);
    connect(buttonBox, &QDialogButtonBox::rejected, this, &SelectorDialog::reject);
    connect(tree, &QTreeView::doubleClicked, this, &SelectorDialog::accept);

    monitor_model_ = new SelectorDialogModel(this);
    monitor_model_->setSourceModel(monitor_);
    tree->setModel(monitor_model_);
    tree->setItemDelegate(new SelectorDialogItemDelegate(tree));
    connect(tree->selectionModel(), &QItemSelectionModel::selectionChanged, this,
            &SelectorDialog::updateSelection);
    tree->header()->setStretchLastSection(false);
    tree->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
    tree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
    tree->header()->setSectionResizeMode(2, QHeaderView::Stretch);

    auto first_board = Monitor::boardFromModel(monitor_model_, 0);
    if (first_board) {
        tree->setCurrentIndex(monitor_->index(0, 0));
    } else {
        buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
    }
}

void SelectorDialog::setExtendedSelection(bool extended)
{
    tree->setSelectionMode(extended
                           ? QAbstractItemView::ExtendedSelection
                           : QAbstractItemView::SingleSelection);
}

void SelectorDialog::setAction(const QString &action)
{
    action_ = action;
    setWindowTitle(QString("%1 | %2").arg(action, QCoreApplication::applicationName()));
}

void SelectorDialog::updateSelection()
{
    selected_boards_.clear();
    auto indexes = tree->selectionModel()->selectedIndexes();
    qSort(indexes);
    for (auto &idx: indexes) {
        if (idx.column() == 0)
            selected_boards_.push_back(Monitor::boardFromModel(monitor_model_, idx));
    }
    buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!selected_boards_.empty());

    emit selectionChanged();
}
<commit_msg>Block secondary boards in board selection dialog<commit_after>/* TyTools - public domain
   Niels Martignène <niels.martignene@protonmail.com>
   https://koromix.dev/tytools

   This software is in the public domain. Where that dedication is not
   recognized, you are granted a perpetual, irrevocable license to copy,
   distribute, and modify this file as you see fit.

   See the LICENSE file for more details. */

#include <QIdentityProxyModel>
#include <QItemDelegate>
#include <QPushButton>

#include "board.hpp"
#include "monitor.hpp"
#include "selector_dialog.hpp"
#include "tycommander.hpp"

using namespace std;

class SelectorDialogModel: public QIdentityProxyModel {
public:
    SelectorDialogModel(QObject *parent = nullptr)
        : QIdentityProxyModel(parent) {}

    int columnCount(const QModelIndex &parent) const override;
    QVariant data(const QModelIndex &index, int role) const override;
    Qt::ItemFlags flags(const QModelIndex &index) const override;
};

class SelectorDialogItemDelegate: public QItemDelegate {
public:
    SelectorDialogItemDelegate(QObject *parent = nullptr)
        : QItemDelegate(parent) {}

    QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};

int SelectorDialogModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return 3;
}

QVariant SelectorDialogModel::data(const QModelIndex &index, int role) const
{
    if (index.column() == Monitor::COLUMN_BOARD) {
        switch (role) {
        case Qt::TextAlignmentRole:
            return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
        }
    } else if (index.column() == Monitor::COLUMN_MODEL) {
        switch (role) {
        case Qt::TextAlignmentRole:
            return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
        }
    } else if (index.column() == Monitor::COLUMN_STATUS) {
        switch (role) {
        case Qt::TextAlignmentRole:
            return QVariant(Qt::AlignRight | Qt::AlignVCenter);
        case Qt::ForegroundRole:
            return QBrush(Qt::darkGray);
        }
    }

    return QIdentityProxyModel::data(index, role);
}

Qt::ItemFlags SelectorDialogModel::flags(const QModelIndex &index) const
{
    auto board = QIdentityProxyModel::data(index, Monitor::ROLE_BOARD).value<Board *>();
    return board->isSecondary() ? 0 : QIdentityProxyModel::flags(index);
}

QSize SelectorDialogItemDelegate::sizeHint(const QStyleOptionViewItem &option,
                                           const QModelIndex &index) const
{
    auto size = QItemDelegate::sizeHint(option, index);
    size.setHeight(24);
    return size;
}

SelectorDialog::SelectorDialog(QWidget *parent)
    : QDialog(parent), monitor_(tyCommander->monitor())
{
    setupUi(this);

    connect(buttonBox, &QDialogButtonBox::accepted, this, &SelectorDialog::accept);
    connect(buttonBox, &QDialogButtonBox::rejected, this, &SelectorDialog::reject);
    connect(tree, &QTreeView::doubleClicked, this, &SelectorDialog::accept);

    monitor_model_ = new SelectorDialogModel(this);
    monitor_model_->setSourceModel(monitor_);
    tree->setModel(monitor_model_);
    tree->setItemDelegate(new SelectorDialogItemDelegate(tree));
    connect(tree->selectionModel(), &QItemSelectionModel::selectionChanged, this,
            &SelectorDialog::updateSelection);
    tree->header()->setStretchLastSection(false);
    tree->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
    tree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
    tree->header()->setSectionResizeMode(2, QHeaderView::Stretch);

    auto first_board = Monitor::boardFromModel(monitor_model_, 0);
    if (first_board) {
        tree->setCurrentIndex(monitor_->index(0, 0));
    } else {
        buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
    }
}

void SelectorDialog::setExtendedSelection(bool extended)
{
    tree->setSelectionMode(extended
                           ? QAbstractItemView::ExtendedSelection
                           : QAbstractItemView::SingleSelection);
}

void SelectorDialog::setAction(const QString &action)
{
    action_ = action;
    setWindowTitle(QString("%1 | %2").arg(action, QCoreApplication::applicationName()));
}

void SelectorDialog::updateSelection()
{
    selected_boards_.clear();
    auto indexes = tree->selectionModel()->selectedIndexes();
    qSort(indexes);
    for (auto &idx: indexes) {
        if (idx.column() == 0)
            selected_boards_.push_back(Monitor::boardFromModel(monitor_model_, idx));
    }
    buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!selected_boards_.empty());

    emit selectionChanged();
}
<|endoftext|>


<commit_before>/*
 * Convert any file descriptor to a pipe by splicing.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifdef __linux

#include "istream_pipe.hxx"
#include "istream_internal.hxx"
#include "fd_util.h"
#include "direct.hxx"
#include "pipe_stock.hxx"
#include "stock.hxx"
#include "gerrno.h"
#include "pool.hxx"
#include "util/Cast.hxx"

#include <daemon/log.h>

#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

struct PipeIstream {
    struct istream output;
    struct istream *input;
    Stock *stock;
    StockItem *stock_item;
    int fds[2];
    size_t piped;
};

static void
pipe_close(PipeIstream *p)
{
    if (p->stock != nullptr) {
        if (p->stock_item != nullptr)
            /* reuse the pipe only if it's empty */
            stock_put(*p->stock_item, p->piped > 0);
    } else {
        if (p->fds[0] >= 0) {
            close(p->fds[0]);
            p->fds[0] = -1;
        }

        if (p->fds[1] >= 0) {
            close(p->fds[1]);
            p->fds[1] = -1;
        }
    }
}

static void
pipe_abort(PipeIstream *p, GError *error)
{
    pipe_close(p);

    if (p->input != nullptr)
        istream_close_handler(p->input);

    istream_deinit_abort(&p->output, error);
}

static ssize_t
pipe_consume(PipeIstream *p)
{
    assert(p->fds[0] >= 0);
    assert(p->piped > 0);
    assert(p->stock_item != nullptr || p->stock == nullptr);

    ssize_t nbytes = istream_invoke_direct(&p->output, FdType::FD_PIPE,
                                           p->fds[0], p->piped);
    if (unlikely(nbytes == ISTREAM_RESULT_BLOCKING ||
                 nbytes == ISTREAM_RESULT_CLOSED))
        /* handler blocks (-2) or pipe was closed (-3) */
        return nbytes;

    if (unlikely(nbytes == ISTREAM_RESULT_ERRNO && errno != EAGAIN)) {
        GError *error = new_error_errno_msg("read from pipe failed");
        pipe_abort(p, error);
        return ISTREAM_RESULT_CLOSED;
    }

    if (nbytes > 0) {
        assert((size_t)nbytes <= p->piped);
        p->piped -= (size_t)nbytes;

        if (p->piped == 0 && p->stock != nullptr) {
            /* if the pipe was drained, return it to the stock, to
               make it available to other streams */

            stock_put(*p->stock_item, false);
            p->stock_item = nullptr;
            p->fds[0] = -1;
            p->fds[1] = -1;
        }

        if (p->piped == 0 && p->input == nullptr) {
            /* p->input has already reported EOF, and we have been
               waiting for the pipe buffer to become empty */
            pipe_close(p);
            istream_deinit_eof(&p->output);
            return ISTREAM_RESULT_CLOSED;
        }
    }

    return nbytes;
}

/*
 * istream handler
 *
 */

static size_t
pipe_input_data(const void *data, size_t length, void *ctx)
{
    PipeIstream *p = (PipeIstream *)ctx;

    assert(p->output.handler != nullptr);

    if (p->piped > 0) {
        ssize_t nbytes = pipe_consume(p);
        if (nbytes == ISTREAM_RESULT_CLOSED)
            return 0;

        if (p->piped > 0 || p->output.handler == nullptr)
            return 0;
    }

    assert(p->piped == 0);

    return istream_invoke_data(&p->output, data, length);
}

static bool
pipe_create(PipeIstream *p)
{
    assert(p->fds[0] < 0);
    assert(p->fds[1] < 0);

    if (p->stock != nullptr) {
        assert(p->stock_item == nullptr);

        GError *error = nullptr;
        p->stock_item = stock_get_now(*p->stock, *p->output.pool, nullptr,
                                      &error);
        if (p->stock_item == nullptr) {
            daemon_log(1, "%s\n", error->message);
            g_error_free(error);
            return false;
        }

        pipe_stock_item_get(p->stock_item, p->fds);
    } else {
        if (pipe_cloexec_nonblock(p->fds) < 0) {
            daemon_log(1, "pipe() failed: %s\n", strerror(errno));
            return false;
        }
    }

    return true;
}

static ssize_t
pipe_input_direct(FdType type, int fd, size_t max_length, void *ctx)
{
    PipeIstream *p = (PipeIstream *)ctx;

    assert(p->output.handler != nullptr);
    assert(p->output.handler->direct != nullptr);
    assert(istream_check_direct(&p->output, FdType::FD_PIPE));

    if (p->piped > 0) {
        ssize_t nbytes = pipe_consume(p);
        if (nbytes <= 0)
            return nbytes;

        if (p->piped > 0)
            /* if the pipe still isn't empty, we can't start reading
               new input */
            return ISTREAM_RESULT_BLOCKING;
    }

    if (istream_check_direct(&p->output, type))
        /* already supported by handler (maybe already a pipe) - no
           need for wrapping it into a pipe */
        return istream_invoke_direct(&p->output, type, fd, max_length);

    assert((type & ISTREAM_TO_PIPE) == type);

    if (p->fds[1] < 0 && !pipe_create(p))
        return ISTREAM_RESULT_CLOSED;

    ssize_t nbytes = splice(fd, nullptr, p->fds[1], nullptr, max_length,
                            SPLICE_F_NONBLOCK | SPLICE_F_MOVE);
    /* don't check EAGAIN here (and don't return -2).  We assume that
       splicing to the pipe cannot possibly block, since we flushed
       the pipe; assume that it can only be the source file which is
       blocking */
    if (nbytes <= 0)
        return nbytes;

    assert(p->piped == 0);
    p->piped = (size_t)nbytes;

    if (pipe_consume(p) == ISTREAM_RESULT_CLOSED)
        return ISTREAM_RESULT_CLOSED;

    return nbytes;
}

static void
pipe_input_eof(void *ctx)
{
    PipeIstream *p = (PipeIstream *)ctx;

    p->input = nullptr;

    if (p->stock == nullptr && p->fds[1] >= 0) {
        close(p->fds[1]);
        p->fds[1] = -1;
    }

    if (p->piped == 0) {
        pipe_close(p);
        istream_deinit_eof(&p->output);
    }
}

static void
pipe_input_abort(GError *error, void *ctx)
{
    PipeIstream *p = (PipeIstream *)ctx;

    pipe_close(p);

    p->input = nullptr;
    istream_deinit_abort(&p->output, error);
}

static const struct istream_handler pipe_input_handler = {
    .data = pipe_input_data,
    .direct = pipe_input_direct,
    .eof = pipe_input_eof,
    .abort = pipe_input_abort,
};

/*
 * istream implementation
 *
 */

static inline PipeIstream *
istream_to_pipe(struct istream *istream)
{
    return &ContainerCast2(*istream, &PipeIstream::output);
}

static off_t
istream_pipe_available(struct istream *istream, bool partial)
{
    PipeIstream *p = istream_to_pipe(istream);

    if (likely(p->input != nullptr)) {
        off_t available = istream_available(p->input, partial);
        if (p->piped > 0) {
            if (available != -1)
                available += p->piped;
            else if (partial)
                available = p->piped;
        }

        return available;
    } else {
        assert(p->piped > 0);

        return p->piped;
    }
}

static void
istream_pipe_read(struct istream *istream)
{
    PipeIstream *p = istream_to_pipe(istream);

    if (p->piped > 0 && (pipe_consume(p) <= 0 || p->piped > 0))
        return;

    /* at this point, the pipe must be flushed - if the pipe is
       flushed, this stream is either closed or there must be an input
       stream */
    assert(p->input != nullptr);

    auto mask = p->output.handler_direct;
    if (mask & FdType::FD_PIPE)
        /* if the handler supports the pipe, we offer our services */
        mask |= ISTREAM_TO_PIPE;

    istream_handler_set_direct(p->input, mask);
    istream_read(p->input);
}

static int
istream_pipe_as_fd(struct istream *istream)
{
    PipeIstream *p = istream_to_pipe(istream);

    if (p->piped > 0)
        /* need to flush the pipe buffer first */
        return -1;

    int fd = istream_as_fd(p->input);
    if (fd >= 0) {
        pipe_close(p);
        istream_deinit(&p->output);
    }

    return fd;
}

static void
istream_pipe_close(struct istream *istream)
{
    PipeIstream *p = istream_to_pipe(istream);

    pipe_close(p);

    if (p->input != nullptr)
        istream_close_handler(p->input);

    istream_deinit(&p->output);
}

static const struct istream_class istream_pipe = {
    .available = istream_pipe_available,
    .read = istream_pipe_read,
    .as_fd = istream_pipe_as_fd,
    .close = istream_pipe_close,
};

/*
 * constructor
 *
 */

struct istream *
istream_pipe_new(struct pool *pool, struct istream *input,
                 Stock *pipe_stock)
{
    assert(input != nullptr);
    assert(!istream_has_handler(input));

    auto *p = NewFromPool<PipeIstream>(*pool);
    istream_init(&p->output, &istream_pipe, pool);

    p->stock = pipe_stock;
    p->stock_item = nullptr;
    p->fds[0] = -1;
    p->fds[1] = -1;
    p->piped = 0;

    istream_assign_handler(&p->input, input,
                           &pipe_input_handler, p,
                           0);

    return &p->output;
}

#endif
<commit_msg>istream/pipe: move functions into the struct<commit_after>/*
 * Convert any file descriptor to a pipe by splicing.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifdef __linux

#include "istream_pipe.hxx"
#include "istream_internal.hxx"
#include "fd_util.h"
#include "direct.hxx"
#include "pipe_stock.hxx"
#include "stock.hxx"
#include "gerrno.h"
#include "pool.hxx"
#include "util/Cast.hxx"

#include <daemon/log.h>

#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

struct PipeIstream {
    struct istream output;
    struct istream *input;
    Stock *stock;
    StockItem *stock_item;
    int fds[2];
    size_t piped;

    void CloseInternal();
    void Abort(GError *error);
    ssize_t Consume();
    bool Create();
};

void
PipeIstream::CloseInternal()
{
    if (stock != nullptr) {
        if (stock_item != nullptr)
            /* reuse the pipe only if it's empty */
            stock_put(*stock_item, piped > 0);
    } else {
        if (fds[0] >= 0) {
            close(fds[0]);
            fds[0] = -1;
        }

        if (fds[1] >= 0) {
            close(fds[1]);
            fds[1] = -1;
        }
    }
}

void
PipeIstream::Abort(GError *error)
{
    CloseInternal();

    if (input != nullptr)
        istream_close_handler(input);

    istream_deinit_abort(&output, error);
}

ssize_t
PipeIstream::Consume()
{
    assert(fds[0] >= 0);
    assert(piped > 0);
    assert(stock_item != nullptr || stock == nullptr);

    ssize_t nbytes = istream_invoke_direct(&output, FdType::FD_PIPE,
                                           fds[0], piped);
    if (unlikely(nbytes == ISTREAM_RESULT_BLOCKING ||
                 nbytes == ISTREAM_RESULT_CLOSED))
        /* handler blocks (-2) or pipe was closed (-3) */
        return nbytes;

    if (unlikely(nbytes == ISTREAM_RESULT_ERRNO && errno != EAGAIN)) {
        GError *error = new_error_errno_msg("read from pipe failed");
        Abort(error);
        return ISTREAM_RESULT_CLOSED;
    }

    if (nbytes > 0) {
        assert((size_t)nbytes <= piped);
        piped -= (size_t)nbytes;

        if (piped == 0 && stock != nullptr) {
            /* if the pipe was drained, return it to the stock, to
               make it available to other streams */

            stock_put(*stock_item, false);
            stock_item = nullptr;
            fds[0] = -1;
            fds[1] = -1;
        }

        if (piped == 0 && input == nullptr) {
            /* our input has already reported EOF, and we have been
               waiting for the pipe buffer to become empty */
            CloseInternal();
            istream_deinit_eof(&output);
            return ISTREAM_RESULT_CLOSED;
        }
    }

    return nbytes;
}

/*
 * istream handler
 *
 */

static size_t
pipe_input_data(const void *data, size_t length, void *ctx)
{
    PipeIstream *p = (PipeIstream *)ctx;

    assert(p->output.handler != nullptr);

    if (p->piped > 0) {
        ssize_t nbytes = p->Consume();
        if (nbytes == ISTREAM_RESULT_CLOSED)
            return 0;

        if (p->piped > 0 || p->output.handler == nullptr)
            return 0;
    }

    assert(p->piped == 0);

    return istream_invoke_data(&p->output, data, length);
}

inline bool
PipeIstream::Create()
{
    assert(fds[0] < 0);
    assert(fds[1] < 0);

    if (stock != nullptr) {
        assert(stock_item == nullptr);

        GError *error = nullptr;
        stock_item = stock_get_now(*stock, *output.pool, nullptr, &error);
        if (stock_item == nullptr) {
            daemon_log(1, "%s\n", error->message);
            g_error_free(error);
            return false;
        }

        pipe_stock_item_get(stock_item, fds);
    } else {
        if (pipe_cloexec_nonblock(fds) < 0) {
            daemon_log(1, "pipe() failed: %s\n", strerror(errno));
            return false;
        }
    }

    return true;
}

static ssize_t
pipe_input_direct(FdType type, int fd, size_t max_length, void *ctx)
{
    PipeIstream *p = (PipeIstream *)ctx;

    assert(p->output.handler != nullptr);
    assert(p->output.handler->direct != nullptr);
    assert(istream_check_direct(&p->output, FdType::FD_PIPE));

    if (p->piped > 0) {
        ssize_t nbytes = p->Consume();
        if (nbytes <= 0)
            return nbytes;

        if (p->piped > 0)
            /* if the pipe still isn't empty, we can't start reading
               new input */
            return ISTREAM_RESULT_BLOCKING;
    }

    if (istream_check_direct(&p->output, type))
        /* already supported by handler (maybe already a pipe) - no
           need for wrapping it into a pipe */
        return istream_invoke_direct(&p->output, type, fd, max_length);

    assert((type & ISTREAM_TO_PIPE) == type);

    if (p->fds[1] < 0 && !p->Create())
        return ISTREAM_RESULT_CLOSED;

    ssize_t nbytes = splice(fd, nullptr, p->fds[1], nullptr, max_length,
                            SPLICE_F_NONBLOCK | SPLICE_F_MOVE);
    /* don't check EAGAIN here (and don't return -2).  We assume that
       splicing to the pipe cannot possibly block, since we flushed
       the pipe; assume that it can only be the source file which is
       blocking */
    if (nbytes <= 0)
        return nbytes;

    assert(p->piped == 0);
    p->piped = (size_t)nbytes;

    if (p->Consume() == ISTREAM_RESULT_CLOSED)
        return ISTREAM_RESULT_CLOSED;

    return nbytes;
}

static void
pipe_input_eof(void *ctx)
{
    PipeIstream *p = (PipeIstream *)ctx;

    p->input = nullptr;

    if (p->stock == nullptr && p->fds[1] >= 0) {
        close(p->fds[1]);
        p->fds[1] = -1;
    }

    if (p->piped == 0) {
        p->CloseInternal();
        istream_deinit_eof(&p->output);
    }
}

static void
pipe_input_abort(GError *error, void *ctx)
{
    PipeIstream *p = (PipeIstream *)ctx;

    p->CloseInternal();

    p->input = nullptr;
    istream_deinit_abort(&p->output, error);
}

static const struct istream_handler pipe_input_handler = {
    .data = pipe_input_data,
    .direct = pipe_input_direct,
    .eof = pipe_input_eof,
    .abort = pipe_input_abort,
};

/*
 * istream implementation
 *
 */

static inline PipeIstream *
istream_to_pipe(struct istream *istream)
{
    return &ContainerCast2(*istream, &PipeIstream::output);
}

static off_t
istream_pipe_available(struct istream *istream, bool partial)
{
    PipeIstream *p = istream_to_pipe(istream);

    if (likely(p->input != nullptr)) {
        off_t available = istream_available(p->input, partial);
        if (p->piped > 0) {
            if (available != -1)
                available += p->piped;
            else if (partial)
                available = p->piped;
        }

        return available;
    } else {
        assert(p->piped > 0);

        return p->piped;
    }
}

static void
istream_pipe_read(struct istream *istream)
{
    PipeIstream *p = istream_to_pipe(istream);

    if (p->piped > 0 && (p->Consume() <= 0 || p->piped > 0))
        return;

    /* at this point, the pipe must be flushed - if the pipe is
       flushed, this stream is either closed or there must be an input
       stream */
    assert(p->input != nullptr);

    auto mask = p->output.handler_direct;
    if (mask & FdType::FD_PIPE)
        /* if the handler supports the pipe, we offer our services */
        mask |= ISTREAM_TO_PIPE;

    istream_handler_set_direct(p->input, mask);
    istream_read(p->input);
}

static int
istream_pipe_as_fd(struct istream *istream)
{
    PipeIstream *p = istream_to_pipe(istream);

    if (p->piped > 0)
        /* need to flush the pipe buffer first */
        return -1;

    int fd = istream_as_fd(p->input);
    if (fd >= 0) {
        p->CloseInternal();
        istream_deinit(&p->output);
    }

    return fd;
}

static void
istream_pipe_close(struct istream *istream)
{
    PipeIstream *p = istream_to_pipe(istream);

    p->CloseInternal();

    if (p->input != nullptr)
        istream_close_handler(p->input);

    istream_deinit(&p->output);
}

static const struct istream_class istream_pipe = {
    .available = istream_pipe_available,
    .read = istream_pipe_read,
    .as_fd = istream_pipe_as_fd,
    .close = istream_pipe_close,
};

/*
 * constructor
 *
 */

struct istream *
istream_pipe_new(struct pool *pool, struct istream *input,
                 Stock *pipe_stock)
{
    assert(input != nullptr);
    assert(!istream_has_handler(input));

    auto *p = NewFromPool<PipeIstream>(*pool);
    istream_init(&p->output, &istream_pipe, pool);

    p->stock = pipe_stock;
    p->stock_item = nullptr;
    p->fds[0] = -1;
    p->fds[1] = -1;
    p->piped = 0;

    istream_assign_handler(&p->input, input,
                           &pipe_input_handler, p,
                           0);

    return &p->output;
}

#endif
<|endoftext|>


<commit_before>/*
 *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */
#include "test/scenario/scenario.h"

#include <atomic>

#include "test/gtest.h"
#include "test/logging/memory_log_writer.h"
#include "test/scenario/stats_collection.h"

namespace webrtc {
namespace test {
TEST(ScenarioTest, StartsAndStopsWithoutErrors) {
  std::atomic<bool> packet_received(false);
  std::atomic<bool> bitrate_changed(false);
  Scenario s;
  CallClientConfig call_client_config;
  call_client_config.transport.rates.start_rate = DataRate::KilobitsPerSec(300);
  auto* alice = s.CreateClient("alice", call_client_config);
  auto* bob = s.CreateClient("bob", call_client_config);
  NetworkSimulationConfig network_config;
  auto alice_net = s.CreateSimulationNode(network_config);
  auto bob_net = s.CreateSimulationNode(network_config);
  auto route = s.CreateRoutes(alice, {alice_net}, bob, {bob_net});

  VideoStreamConfig video_stream_config;
  s.CreateVideoStream(route->forward(), video_stream_config);
  s.CreateVideoStream(route->reverse(), video_stream_config);

  AudioStreamConfig audio_stream_config;
  audio_stream_config.encoder.min_rate = DataRate::KilobitsPerSec(6);
  audio_stream_config.encoder.max_rate = DataRate::KilobitsPerSec(64);
  audio_stream_config.encoder.allocate_bitrate = true;
  audio_stream_config.stream.in_bandwidth_estimation = false;
  s.CreateAudioStream(route->forward(), audio_stream_config);
  s.CreateAudioStream(route->reverse(), audio_stream_config);

  RandomWalkConfig cross_traffic_config;
  s.net()->CreateRandomWalkCrossTraffic(
      s.net()->CreateTrafficRoute({alice_net}), cross_traffic_config);

  s.NetworkDelayedAction({alice_net, bob_net}, 100,
                         [&packet_received] { packet_received = true; });
  s.Every(TimeDelta::Millis(10), [alice, bob, &bitrate_changed] {
    if (alice->GetStats().send_bandwidth_bps != 300000 &&
        bob->GetStats().send_bandwidth_bps != 300000)
      bitrate_changed = true;
  });
  s.RunUntil(TimeDelta::Seconds(2), TimeDelta::Millis(5),
             [&bitrate_changed, &packet_received] {
               return packet_received && bitrate_changed;
             });
  EXPECT_TRUE(packet_received);
  EXPECT_TRUE(bitrate_changed);
}
namespace {
void SetupVideoCall(Scenario& s, VideoQualityAnalyzer* analyzer) {
  CallClientConfig call_config;
  auto* alice = s.CreateClient("alice", call_config);
  auto* bob = s.CreateClient("bob", call_config);
  NetworkSimulationConfig network_config;
  network_config.bandwidth = DataRate::KilobitsPerSec(1000);
  network_config.delay = TimeDelta::Millis(50);
  auto alice_net = s.CreateSimulationNode(network_config);
  auto bob_net = s.CreateSimulationNode(network_config);
  auto route = s.CreateRoutes(alice, {alice_net}, bob, {bob_net});
  VideoStreamConfig video;
  if (analyzer) {
    video.source.capture = VideoStreamConfig::Source::Capture::kVideoFile;
    video.source.video_file.name = "foreman_cif";
    video.source.video_file.width = 352;
    video.source.video_file.height = 288;
    video.source.framerate = 30;
    video.encoder.codec = VideoStreamConfig::Encoder::Codec::kVideoCodecVP8;
    video.encoder.implementation =
        VideoStreamConfig::Encoder::Implementation::kSoftware;
    video.hooks.frame_pair_handlers = {analyzer->Handler()};
  }
  s.CreateVideoStream(route->forward(), video);
  s.CreateAudioStream(route->forward(), AudioStreamConfig());
}
}  // namespace

TEST(ScenarioTest, SimTimeEncoding) {
  VideoQualityAnalyzerConfig analyzer_config;
  analyzer_config.psnr_coverage = 0.1;
  VideoQualityAnalyzer analyzer(analyzer_config);
  {
    Scenario s("scenario/encode_sim", false);
    SetupVideoCall(s, &analyzer);
    s.RunFor(TimeDelta::Seconds(2));
  }
  // Regression tests based on previous runs.
  EXPECT_EQ(analyzer.stats().lost_count, 0);
  EXPECT_NEAR(analyzer.stats().psnr_with_freeze.Mean(), 38, 5);
}

// TODO(bugs.webrtc.org/10515): Remove this when performance has been improved.
#if defined(WEBRTC_IOS) && defined(WEBRTC_ARCH_ARM64) && !defined(NDEBUG)
#define MAYBE_RealTimeEncoding DISABLED_RealTimeEncoding
#else
#define MAYBE_RealTimeEncoding RealTimeEncoding
#endif
TEST(ScenarioTest, MAYBE_RealTimeEncoding) {
  VideoQualityAnalyzerConfig analyzer_config;
  analyzer_config.psnr_coverage = 0.1;
  VideoQualityAnalyzer analyzer(analyzer_config);
  {
    Scenario s("scenario/encode_real", true);
    SetupVideoCall(s, &analyzer);
    s.RunFor(TimeDelta::Seconds(2));
  }
  // Regression tests based on previous runs.
  EXPECT_LT(analyzer.stats().lost_count, 2);
  // This far below expected but ensures that we get something.
  EXPECT_GT(analyzer.stats().psnr_with_freeze.Mean(), 10);
}

TEST(ScenarioTest, SimTimeFakeing) {
  Scenario s("scenario/encode_sim", false);
  SetupVideoCall(s, nullptr);
  s.RunFor(TimeDelta::Seconds(2));
}

TEST(ScenarioTest, WritesToRtcEventLog) {
  MemoryLogStorage storage;
  {
    Scenario s(storage.CreateFactory(), false);
    SetupVideoCall(s, nullptr);
    s.RunFor(TimeDelta::Seconds(1));
  }
  auto logs = storage.logs();
  // We expect that a rtc event log has been created and that it has some data.
  EXPECT_GE(storage.logs().at("alice.rtc.dat").size(), 1u);
}

}  // namespace test
}  // namespace webrtc
<commit_msg>Adds test case that would have found potential dead-lock in pacer.<commit_after>/*
 *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */
#include "test/scenario/scenario.h"

#include <atomic>

#include "test/field_trial.h"
#include "test/gtest.h"
#include "test/logging/memory_log_writer.h"
#include "test/scenario/stats_collection.h"

namespace webrtc {
namespace test {
TEST(ScenarioTest, StartsAndStopsWithoutErrors) {
  std::atomic<bool> packet_received(false);
  std::atomic<bool> bitrate_changed(false);
  Scenario s;
  CallClientConfig call_client_config;
  call_client_config.transport.rates.start_rate = DataRate::KilobitsPerSec(300);
  auto* alice = s.CreateClient("alice", call_client_config);
  auto* bob = s.CreateClient("bob", call_client_config);
  NetworkSimulationConfig network_config;
  auto alice_net = s.CreateSimulationNode(network_config);
  auto bob_net = s.CreateSimulationNode(network_config);
  auto route = s.CreateRoutes(alice, {alice_net}, bob, {bob_net});

  VideoStreamConfig video_stream_config;
  s.CreateVideoStream(route->forward(), video_stream_config);
  s.CreateVideoStream(route->reverse(), video_stream_config);

  AudioStreamConfig audio_stream_config;
  audio_stream_config.encoder.min_rate = DataRate::KilobitsPerSec(6);
  audio_stream_config.encoder.max_rate = DataRate::KilobitsPerSec(64);
  audio_stream_config.encoder.allocate_bitrate = true;
  audio_stream_config.stream.in_bandwidth_estimation = false;
  s.CreateAudioStream(route->forward(), audio_stream_config);
  s.CreateAudioStream(route->reverse(), audio_stream_config);

  RandomWalkConfig cross_traffic_config;
  s.net()->CreateRandomWalkCrossTraffic(
      s.net()->CreateTrafficRoute({alice_net}), cross_traffic_config);

  s.NetworkDelayedAction({alice_net, bob_net}, 100,
                         [&packet_received] { packet_received = true; });
  s.Every(TimeDelta::Millis(10), [alice, bob, &bitrate_changed] {
    if (alice->GetStats().send_bandwidth_bps != 300000 &&
        bob->GetStats().send_bandwidth_bps != 300000)
      bitrate_changed = true;
  });
  s.RunUntil(TimeDelta::Seconds(2), TimeDelta::Millis(5),
             [&bitrate_changed, &packet_received] {
               return packet_received && bitrate_changed;
             });
  EXPECT_TRUE(packet_received);
  EXPECT_TRUE(bitrate_changed);
}
namespace {
void SetupVideoCall(Scenario& s, VideoQualityAnalyzer* analyzer) {
  CallClientConfig call_config;
  auto* alice = s.CreateClient("alice", call_config);
  auto* bob = s.CreateClient("bob", call_config);
  NetworkSimulationConfig network_config;
  network_config.bandwidth = DataRate::KilobitsPerSec(1000);
  network_config.delay = TimeDelta::Millis(50);
  auto alice_net = s.CreateSimulationNode(network_config);
  auto bob_net = s.CreateSimulationNode(network_config);
  auto route = s.CreateRoutes(alice, {alice_net}, bob, {bob_net});
  VideoStreamConfig video;
  if (analyzer) {
    video.source.capture = VideoStreamConfig::Source::Capture::kVideoFile;
    video.source.video_file.name = "foreman_cif";
    video.source.video_file.width = 352;
    video.source.video_file.height = 288;
    video.source.framerate = 30;
    video.encoder.codec = VideoStreamConfig::Encoder::Codec::kVideoCodecVP8;
    video.encoder.implementation =
        VideoStreamConfig::Encoder::Implementation::kSoftware;
    video.hooks.frame_pair_handlers = {analyzer->Handler()};
  }
  s.CreateVideoStream(route->forward(), video);
  s.CreateAudioStream(route->forward(), AudioStreamConfig());
}
}  // namespace

TEST(ScenarioTest, SimTimeEncoding) {
  VideoQualityAnalyzerConfig analyzer_config;
  analyzer_config.psnr_coverage = 0.1;
  VideoQualityAnalyzer analyzer(analyzer_config);
  {
    Scenario s("scenario/encode_sim", false);
    SetupVideoCall(s, &analyzer);
    s.RunFor(TimeDelta::Seconds(2));
  }
  // Regression tests based on previous runs.
  EXPECT_EQ(analyzer.stats().lost_count, 0);
  EXPECT_NEAR(analyzer.stats().psnr_with_freeze.Mean(), 38, 5);
}

// TODO(bugs.webrtc.org/10515): Remove this when performance has been improved.
#if defined(WEBRTC_IOS) && defined(WEBRTC_ARCH_ARM64) && !defined(NDEBUG)
#define MAYBE_RealTimeEncoding DISABLED_RealTimeEncoding
#else
#define MAYBE_RealTimeEncoding RealTimeEncoding
#endif
TEST(ScenarioTest, MAYBE_RealTimeEncoding) {
  VideoQualityAnalyzerConfig analyzer_config;
  analyzer_config.psnr_coverage = 0.1;
  VideoQualityAnalyzer analyzer(analyzer_config);
  {
    Scenario s("scenario/encode_real", true);
    SetupVideoCall(s, &analyzer);
    s.RunFor(TimeDelta::Seconds(2));
  }
  // Regression tests based on previous runs.
  EXPECT_LT(analyzer.stats().lost_count, 2);
  // This far below expected but ensures that we get something.
  EXPECT_GT(analyzer.stats().psnr_with_freeze.Mean(), 10);
}

TEST(ScenarioTest, SimTimeFakeing) {
  Scenario s("scenario/encode_sim", false);
  SetupVideoCall(s, nullptr);
  s.RunFor(TimeDelta::Seconds(2));
}

TEST(ScenarioTest, WritesToRtcEventLog) {
  MemoryLogStorage storage;
  {
    Scenario s(storage.CreateFactory(), false);
    SetupVideoCall(s, nullptr);
    s.RunFor(TimeDelta::Seconds(1));
  }
  auto logs = storage.logs();
  // We expect that a rtc event log has been created and that it has some data.
  EXPECT_GE(storage.logs().at("alice.rtc.dat").size(), 1u);
}

TEST(ScenarioTest,
     RetransmitsVideoPacketsInAudioAndVideoCallWithSendSideBweAndLoss) {
  // Make sure audio packets are included in transport feedback.
  test::ScopedFieldTrials override_field_trials(
      "WebRTC-Audio-SendSideBwe/Enabled/WebRTC-Audio-ABWENoTWCC/Disabled/");

  Scenario s;
  CallClientConfig call_client_config;
  call_client_config.transport.rates.start_rate = DataRate::KilobitsPerSec(300);
  auto* alice = s.CreateClient("alice", call_client_config);
  auto* bob = s.CreateClient("bob", call_client_config);
  NetworkSimulationConfig network_config;
  // Add some loss and delay.
  network_config.delay = TimeDelta::Millis(200);
  network_config.loss_rate = 0.05;
  auto alice_net = s.CreateSimulationNode(network_config);
  auto bob_net = s.CreateSimulationNode(network_config);
  auto route = s.CreateRoutes(alice, {alice_net}, bob, {bob_net});

  // First add an audio stream, then a video stream.
  // Needed to make sure audio RTP module is selected first when sending
  // transport feedback message.
  AudioStreamConfig audio_stream_config;
  audio_stream_config.encoder.min_rate = DataRate::KilobitsPerSec(6);
  audio_stream_config.encoder.max_rate = DataRate::KilobitsPerSec(64);
  audio_stream_config.encoder.allocate_bitrate = true;
  audio_stream_config.stream.in_bandwidth_estimation = true;
  s.CreateAudioStream(route->forward(), audio_stream_config);
  s.CreateAudioStream(route->reverse(), audio_stream_config);

  VideoStreamConfig video_stream_config;
  auto video = s.CreateVideoStream(route->forward(), video_stream_config);
  s.CreateVideoStream(route->reverse(), video_stream_config);

  // Run for 10 seconds.
  s.RunFor(TimeDelta::Seconds(10));
  // Make sure retransmissions have happened.
  int retransmit_packets = 0;
  for (const auto& substream : video->send()->GetStats().substreams) {
    retransmit_packets += substream.second.rtp_stats.retransmitted.packets;
  }
  EXPECT_GT(retransmit_packets, 0);
}

}  // namespace test
}  // namespace webrtc
<|endoftext|>