| Index: lib/DispatcherBase_cpp.template | 
| diff --git a/lib/DispatcherBase_cpp.template b/lib/DispatcherBase_cpp.template | 
| index 9095cbc961c7761406eb58c76264cf13e9b1f128..b08947aba43f32c1da32a290560dcbd32471e306 100644 | 
| --- a/lib/DispatcherBase_cpp.template | 
| +++ b/lib/DispatcherBase_cpp.template | 
| @@ -39,6 +39,16 @@ DispatchResponse DispatchResponse::InternalError() | 
| } | 
|  | 
| // static | 
| +DispatchResponse DispatchResponse::InvalidParams(const String& error) | 
| +{ | 
| +    DispatchResponse result; | 
| +    result.m_status = kError; | 
| +    result.m_errorCode = kInvalidParams; | 
| +    result.m_errorMessage = error; | 
| +    return result; | 
| +} | 
| + | 
| +// static | 
| DispatchResponse DispatchResponse::FallThrough() | 
| { | 
| DispatchResponse result; | 
| @@ -58,9 +68,10 @@ DispatcherBase::WeakPtr::~WeakPtr() | 
| m_dispatcher->m_weakPtrs.erase(this); | 
| } | 
|  | 
| -DispatcherBase::Callback::Callback(std::unique_ptr<DispatcherBase::WeakPtr> backendImpl, int callId) | 
| +DispatcherBase::Callback::Callback(std::unique_ptr<DispatcherBase::WeakPtr> backendImpl, int callId, int callbackId) | 
| : m_backendImpl(std::move(backendImpl)) | 
| -    , m_callId(callId) { } | 
| +    , m_callId(callId) | 
| +    , m_callbackId(callbackId) { } | 
|  | 
| DispatcherBase::Callback::~Callback() = default; | 
|  | 
| @@ -77,14 +88,36 @@ void DispatcherBase::Callback::sendIfActive(std::unique_ptr<protocol::Dictionary | 
| m_backendImpl = nullptr; | 
| } | 
|  | 
| +void DispatcherBase::Callback::fallThroughIfActive() | 
| +{ | 
| +    if (!m_backendImpl || !m_backendImpl->get()) | 
| +        return; | 
| +    m_backendImpl->get()->markFallThrough(m_callbackId); | 
| +    m_backendImpl = nullptr; | 
| +} | 
| + | 
| DispatcherBase::DispatcherBase(FrontendChannel* frontendChannel) | 
| -    : m_frontendChannel(frontendChannel) { } | 
| +    : m_frontendChannel(frontendChannel) | 
| +    , m_lastCallbackId(0) | 
| +    , m_lastCallbackFallThrough(false) { } | 
|  | 
| DispatcherBase::~DispatcherBase() | 
| { | 
| clearFrontend(); | 
| } | 
|  | 
| +int DispatcherBase::nextCallbackId() | 
| +{ | 
| +    m_lastCallbackFallThrough = false; | 
| +    return ++m_lastCallbackId; | 
| +} | 
| + | 
| +void DispatcherBase::markFallThrough(int callbackId) | 
| +{ | 
| +    DCHECK(callbackId == m_lastCallbackId); | 
| +    m_lastCallbackFallThrough = true; | 
| +} | 
| + | 
| // static | 
| bool DispatcherBase::getCommandName(const String& message, String* result) | 
| { | 
| @@ -169,7 +202,13 @@ std::unique_ptr<DispatcherBase::WeakPtr> DispatcherBase::weakPtr() | 
| } | 
|  | 
| UberDispatcher::UberDispatcher(FrontendChannel* frontendChannel) | 
| -    : m_frontendChannel(frontendChannel) { } | 
| +    : m_frontendChannel(frontendChannel) | 
| +    , m_fallThroughForNotFound(false) { } | 
| + | 
| +void UberDispatcher::setFallThroughForNotFound(bool fallThroughForNotFound) | 
| +{ | 
| +    m_fallThroughForNotFound = fallThroughForNotFound; | 
| +} | 
|  | 
| void UberDispatcher::registerBackend(const String& name, std::unique_ptr<protocol::DispatcherBase> dispatcher) | 
| { | 
| @@ -206,12 +245,16 @@ DispatchResponse::Status UberDispatcher::dispatch(std::unique_ptr<Value> parsedM | 
|  | 
| size_t dotIndex = method.find("."); | 
| if (dotIndex == StringUtil::kNotFound) { | 
| +        if (m_fallThroughForNotFound) | 
| +            return DispatchResponse::kFallThrough; | 
| reportProtocolErrorTo(m_frontendChannel, callId, DispatchResponse::kMethodNotFound, "'" + method + "' wasn't found", nullptr); | 
| return DispatchResponse::kError; | 
| } | 
| String domain = StringUtil::substring(method, 0, dotIndex); | 
| auto it = m_dispatchers.find(domain); | 
| if (it == m_dispatchers.end()) { | 
| +        if (m_fallThroughForNotFound) | 
| +            return DispatchResponse::kFallThrough; | 
| reportProtocolErrorTo(m_frontendChannel, callId, DispatchResponse::kMethodNotFound, "'" + method + "' wasn't found", nullptr); | 
| return DispatchResponse::kError; | 
| } | 
|  |