void MediaSourceImpl::EnsureBuffering() { switch (EnsureBufferingInternal()) { case NONE: break; case STARTED: m_state = STARTED; break; case IDLE: if (VerifyContentType()) { m_state = IDLE; for (OpListenersIterator iter(m_listeners); m_listeners.HasNext(iter);) m_listeners.GetNext(iter)->OnIdle(this); } else { OP_ASSERT(m_state == FAILED); } break; case FAILED: LoadFailed(); break; default: OP_ASSERT(!"Unexpected state change"); } }
/* virtual */ void MediaSourceImpl::HandleCallback(OpMessage msg, MH_PARAM_1 par1, MH_PARAM_2 par2) { if (msg == MSG_MEDIA_SOURCE_ENSURE_BUFFERING) { OP_ASSERT(m_pending_ensure); EnsureBuffering(); m_pending_ensure = FALSE; return; } else if (msg == MSG_MEDIA_SOURCE_NOTIFY_LISTENER) { // The listener may have been removed while the message was // pending, so find it in the listeners list before notifying. MediaProgressListener* listener = reinterpret_cast<MediaProgressListener*>(par2); for (OpListenersIterator iter(m_listeners); m_listeners.HasNext(iter);) { if (m_listeners.GetNext(iter) == listener) { if (m_state == FAILED) listener->OnError(this); else if (m_state == IDLE) listener->OnIdle(this); // else: The state has already changed and the // listener was notified just like the others. break; } } return; } // URL_DataDescriptor::RetrieveData posts MSG_URL_DATA_LOADED when // reading, which we are not interested in when idle or paused. // URL messages after an error are irrelevant to us. if (m_state == IDLE || m_state == PAUSED || m_state == FAILED) return; // URL messages in the message queue when calling StopLoading // will arrive after starting a new request, at which point they // must be ignored. Wait for headers/failure/redirect. if (m_state == STARTED && msg == MSG_URL_DATA_LOADED) return; OP_ASSERT(m_state == STARTED || m_state == HEADERS || m_state == LOADING); switch(msg) { case MSG_HEADER_LOADED: OP_ASSERT(m_state == STARTED); m_state = HEADERS; // If we are streaming, tell all listeners except the first to // restart loading, at which point they'll get a new source. if (IsStreaming()) { OpListenersIterator iter(m_listeners); m_listeners.GetNext(iter); while (m_listeners.HasNext(iter)) m_listeners.GetNext(iter)->OnClientCollision(this); } break; case MSG_URL_DATA_LOADED: if (m_state == HEADERS) m_state = LOADING; OP_ASSERT(m_state == LOADING); if (IsSuccessURL(m_use_url)) { // This point will be reached very often while the // transfer is progressing normally, so it is important // that the following steps are not needlessly wasteful. if (!VerifyContentType()) return; // EnsureBuffering does a non-trivial amount of work, so // only call it if (a) the request has finished or (b) the // current request is "too big" and needs clamping. if (m_clamp_request || IsLoadedURL(m_use_url)) { EnsureBuffering(); if (m_state != LOADING) break; } // The listeners are responsible for throttling the // side-effects buffering progress. for (OpListenersIterator iter(m_listeners); m_listeners.HasNext(iter);) m_listeners.GetNext(iter)->OnProgress(this); if (IsStreaming()) { BOOL available; OpFileLength remaining; GetStreamingCoverage(available, remaining); if (available && remaining < STREAMING_CACHE_PAUSE_LIMIT) PauseBuffering(); } } else { LoadFailed(); } break; case MSG_URL_LOADING_FAILED: // Try to recover from a network errors that happen while // loading, but not those that happen before (or after) that. if (m_state == LOADING && IsResumableURL(m_use_url)) { StopBuffering(); m_state = IDLE; EnsureBuffering(); } else { LoadFailed(); } break; case MSG_URL_MOVED: { URL moved_to = m_use_url->GetAttribute(URL::KMovedToURL, URL::KFollowRedirect); if (!moved_to.IsEmpty()) { OP_ASSERT(m_use_url->Id(URL::KFollowRedirect) == (URL_ID)par2 && moved_to.Id(URL::KNoRedirect) == (URL_ID)par2); m_use_url.SetURL(moved_to); OP_DELETE(m_url_dd); m_url_dd = NULL; m_message_handler->UnsetCallBacks(this); RAISE_AND_RETURN_VOID_IF_ERROR(SetCallBacks()); } } break; default: OP_ASSERT(FALSE); } // Ensure that we are notified of further buffering progress. if (m_url_dd) m_url_dd->ClearPostedMessage(); }
void ThebesLayerD3D10::Validate(ReadbackProcessor *aReadback) { if (mVisibleRegion.IsEmpty()) { return; } nsIntRect newTextureRect = mVisibleRegion.GetBounds(); SurfaceMode mode = GetSurfaceMode(); if (mode == SURFACE_COMPONENT_ALPHA && (!mParent || !mParent->SupportsComponentAlphaChildren())) { mode = SURFACE_SINGLE_CHANNEL_ALPHA; } // If we have a transform that requires resampling of our texture, then // we need to make sure we don't sample pixels that haven't been drawn. // We clamp sample coordinates to the texture rect, but when the visible region // doesn't fill the entire texture rect we need to make sure we draw all the // pixels in the texture rect anyway in case they get sampled. nsIntRegion neededRegion = mVisibleRegion; if (!neededRegion.GetBounds().IsEqualInterior(newTextureRect) || neededRegion.GetNumRects() > 1) { gfxMatrix transform2d; if (!GetEffectiveTransform().Is2D(&transform2d) || transform2d.HasNonIntegerTranslation()) { neededRegion = newTextureRect; if (mode == SURFACE_OPAQUE) { // We're going to paint outside the visible region, but layout hasn't // promised that it will paint opaquely there, so we'll have to // treat this layer as transparent. mode = SURFACE_SINGLE_CHANNEL_ALPHA; } } } mCurrentSurfaceMode = mode; VerifyContentType(mode); nsTArray<ReadbackProcessor::Update> readbackUpdates; nsIntRegion readbackRegion; if (aReadback && UsedForReadback()) { aReadback->GetThebesLayerUpdates(this, &readbackUpdates, &readbackRegion); } if (mTexture) { if (!mTextureRect.IsEqualInterior(newTextureRect)) { nsRefPtr<ID3D10Texture2D> oldTexture = mTexture; mTexture = nullptr; nsRefPtr<ID3D10Texture2D> oldTextureOnWhite = mTextureOnWhite; mTextureOnWhite = nullptr; nsIntRegion retainRegion = mTextureRect; // Old visible region will become the region that is covered by both the // old and the new visible region. retainRegion.And(retainRegion, mVisibleRegion); // No point in retaining parts which were not valid. retainRegion.And(retainRegion, mValidRegion); CreateNewTextures(gfxIntSize(newTextureRect.width, newTextureRect.height), mode); nsIntRect largeRect = retainRegion.GetLargestRectangle(); // If we had no hardware texture before, or have no retained area larger than // the retention threshold, we're not retaining and are done here. // If our texture creation failed this can mean a device reset is pending // and we should silently ignore the failure. In the future when device // failures are properly handled we should test for the type of failure // and gracefully handle different failures. See bug 569081. if (!oldTexture || !mTexture || largeRect.width * largeRect.height < RETENTION_THRESHOLD) { mValidRegion.SetEmpty(); } else { CopyRegion(oldTexture, mTextureRect.TopLeft(), mTexture, newTextureRect.TopLeft(), retainRegion, &mValidRegion); if (oldTextureOnWhite) { CopyRegion(oldTextureOnWhite, mTextureRect.TopLeft(), mTextureOnWhite, newTextureRect.TopLeft(), retainRegion, &mValidRegion); } } } } mTextureRect = newTextureRect; if (!mTexture || (mode == SURFACE_COMPONENT_ALPHA && !mTextureOnWhite)) { CreateNewTextures(gfxIntSize(newTextureRect.width, newTextureRect.height), mode); mValidRegion.SetEmpty(); } nsIntRegion drawRegion; drawRegion.Sub(neededRegion, mValidRegion); if (!drawRegion.IsEmpty()) { LayerManagerD3D10::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); if (!cbInfo.Callback) { NS_ERROR("D3D10 should never need to update ThebesLayers in an empty transaction"); return; } DrawRegion(drawRegion, mode); if (readbackUpdates.Length() > 0) { CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, newTextureRect.width, newTextureRect.height, 1, 1, 0, D3D10_USAGE_STAGING, D3D10_CPU_ACCESS_READ); nsRefPtr<ID3D10Texture2D> readbackTexture; HRESULT hr = device()->CreateTexture2D(&desc, NULL, getter_AddRefs(readbackTexture)); if (FAILED(hr)) { LayerManagerD3D10::ReportFailure(NS_LITERAL_CSTRING("ThebesLayerD3D10::Validate(): Failed to create texture"), hr); return; } device()->CopyResource(readbackTexture, mTexture); for (uint32_t i = 0; i < readbackUpdates.Length(); i++) { mD3DManager->readbackManager()->PostTask(readbackTexture, &readbackUpdates[i], gfxPoint(newTextureRect.x, newTextureRect.y)); } } mValidRegion = neededRegion; } }