Commit message: None
CVE: CVE-2018-6047
CWE: CWE-20
Vulnerability description: Insufficient policy enforcement in WebGL in Google Chrome prior to 64.0.3282.119 allowed a remote attacker to potentially leak user redirect URL via a crafted HTML page.

Project: Chrome

void WebGLRenderingContextBase::LRUCanvasResourceProviderCache::BubbleToFront(
    int idx) {
  for (int i = idx; i > 0; --i)
    resource_providers_[i].swap(resource_providers_[i - 1]);
}

Project: Chrome

bool WebGLRenderingContextBase::CopyRenderingResultsFromDrawingBuffer(
    AcceleratedImageBufferSurface* webgl_buffer,
    SourceDrawingBuffer source_buffer) const {
  if (!drawing_buffer_)
    return false;
  std::unique_ptr<WebGraphicsContext3DProvider> provider =
      Platform::Current()->CreateSharedOffscreenGraphicsContext3DProvider();
  if (!provider)
    return false;
  gpu::gles2::GLES2Interface* gl = provider->ContextGL();
  GLuint texture_id = webgl_buffer->GetBackingTextureHandleForOverwrite();
  if (!texture_id)
    return false;

  gl->Flush();

  return drawing_buffer_->CopyToPlatformTexture(
      gl, GL_TEXTURE_2D, texture_id, true, false, IntPoint(0, 0),
      IntRect(IntPoint(0, 0), drawing_buffer_->Size()), source_buffer);
}

Project: Chrome

CreateContextProviderOnWorkerThread(
    Platform::ContextAttributes context_attributes,
    Platform::GraphicsInfo* gl_info,
    bool* using_gpu_compositing,
    const KURL& url) {
  WaitableEvent waitable_event;
  ContextProviderCreationInfo creation_info;
  creation_info.context_attributes = context_attributes;
  creation_info.gl_info = gl_info;
  creation_info.url = url.Copy();
  creation_info.using_gpu_compositing = using_gpu_compositing;
  scoped_refptr<WebTaskRunner> task_runner =
      Platform::Current()->MainThread()->GetWebTaskRunner();
  PostCrossThreadTask(*task_runner, FROM_HERE,
                      CrossThreadBind(&CreateContextProviderOnMainThread,
                                      CrossThreadUnretained(&creation_info),
                                      CrossThreadUnretained(&waitable_event)));
  waitable_event.Wait();
  return std::move(creation_info.created_context_provider);
}

Project: Chrome

void WebGLRenderingContextBase::DispatchContextLostEvent(TimerBase*) {
  WebGLContextEvent* event = WebGLContextEvent::Create(
      EventTypeNames::webglcontextlost, false, true, "");
  Host()->HostDispatchEvent(event);
  restore_allowed_ = event->defaultPrevented();
  if (restore_allowed_ && !is_hidden_) {
    if (auto_recovery_method_ == kAuto)
      restore_timer_.StartOneShot(TimeDelta(), FROM_HERE);
  }
}

Project: Chrome

scoped_refptr<Image> WebGLRenderingContextBase::DrawImageIntoBuffer(
    scoped_refptr<Image> pass_image,
    int width,
    int height,
    const char* function_name) {
  scoped_refptr<Image> image(std::move(pass_image));
  DCHECK(image);

  IntSize size(width, height);
  CanvasResourceProvider* resource_provider =
      generated_image_cache_.GetCanvasResourceProvider(size);
  if (!resource_provider) {
    SynthesizeGLError(GL_OUT_OF_MEMORY, function_name, "out of memory");
    return nullptr;
  }

  if (!image->CurrentFrameKnownToBeOpaque())
    resource_provider->Canvas()->clear(SK_ColorTRANSPARENT);

  IntRect src_rect(IntPoint(), image->Size());
  IntRect dest_rect(0, 0, size.Width(), size.Height());
  PaintFlags flags;
  image->Draw(resource_provider->Canvas(), flags, dest_rect, src_rect,
              kDoNotRespectImageOrientation,
              Image::kDoNotClampImageToSourceRect, Image::kSyncDecode);
  return resource_provider->Snapshot();
}

Project: Chrome

scoped_refptr<StaticBitmapImage> WebGLRenderingContextBase::GetImage(
    AccelerationHint hint) const {
  if (!GetDrawingBuffer())
    return nullptr;
  if (IsMainThread()) {
    GetDrawingBuffer()->ResolveAndBindForReadAndDraw();
    IntSize size = ClampedCanvasSize();
    std::unique_ptr<AcceleratedImageBufferSurface> surface =
        std::make_unique<AcceleratedImageBufferSurface>(size, ColorParams());
    if (!surface->IsValid())
      return nullptr;
    if (!CopyRenderingResultsFromDrawingBuffer(surface.get(), kBackBuffer)) {
      NOTREACHED();
      return nullptr;
    }
    return surface->NewImageSnapshot(hint);
  }

  int width = GetDrawingBuffer()->Size().Width();
  int height = GetDrawingBuffer()->Size().Height();
  SkImageInfo image_info = SkImageInfo::Make(
      width, height, kRGBA_8888_SkColorType,
      CreationAttributes().alpha() ? kPremul_SkAlphaType : kOpaque_SkAlphaType);
  return this->MakeImageSnapshot(image_info);
}

Project: Chrome

    LRUCanvasResourceProviderCache(int capacity)
    : resource_providers_(
          std::make_unique<std::unique_ptr<CanvasResourceProvider>[]>(
              capacity)),
      capacity_(capacity) {}

Project: Chrome

void WebGLRenderingContextBase::MaybeRestoreContext(TimerBase*) {
  DCHECK(isContextLost());

  if (!restore_allowed_)
    return;

  if (canvas()) {
    LocalFrame* frame = canvas()->GetDocument().GetFrame();
    if (!frame)
      return;
    if (frame->Client()->ShouldBlockWebGL())
      return;

    Settings* settings = frame->GetSettings();
    if (settings && ((version_ == 1 && !settings->GetWebGL1Enabled()) ||
                     (version_ == 2 && !settings->GetWebGL2Enabled()))) {
      return;
    }
  }

  if (GetDrawingBuffer()) {
    drawing_buffer_->BeginDestruction();
    drawing_buffer_ = nullptr;
  }

  auto execution_context = Host()->GetTopExecutionContext();
  Platform::ContextAttributes attributes = ToPlatformContextAttributes(
      CreationAttributes(), Version(),
      SupportOwnOffscreenSurface(execution_context));
  Platform::GraphicsInfo gl_info;
  std::unique_ptr<WebGraphicsContext3DProvider> context_provider;
  bool using_gpu_compositing;
  const auto& url = Host()->GetExecutionContextUrl();

  if (IsMainThread()) {
    using_gpu_compositing = !Platform::Current()->IsGpuCompositingDisabled();
    context_provider =
        Platform::Current()->CreateOffscreenGraphicsContext3DProvider(
            attributes, url, nullptr, &gl_info);
  } else {
    context_provider = CreateContextProviderOnWorkerThread(
        attributes, &gl_info, &using_gpu_compositing, url);
  }
  scoped_refptr<DrawingBuffer> buffer;
  if (context_provider && context_provider->BindToCurrentThread()) {
    buffer =
        CreateDrawingBuffer(std::move(context_provider), using_gpu_compositing);
  }
  if (!buffer) {
    if (context_lost_mode_ == kRealLostContext) {
      restore_timer_.StartOneShot(kSecondsBetweenRestoreAttempts, FROM_HERE);
    } else {
      SynthesizeGLError(GL_INVALID_OPERATION, "", "error restoring context");
    }
    return;
  }

  drawing_buffer_ = std::move(buffer);
  GetDrawingBuffer()->Bind(GL_FRAMEBUFFER);
  lost_context_errors_.clear();
  context_lost_mode_ = kNotLostContext;
  auto_recovery_method_ = kManual;
  restore_allowed_ = false;
  RemoveFromEvictedList(this);

  SetupFlags();
  InitializeNewContext();
  MarkContextChanged(kCanvasContextChanged);
  WebGLContextEvent* event = WebGLContextEvent::Create(
      EventTypeNames::webglcontextrestored, false, true, "");
  Host()->HostDispatchEvent(event);
}

Project: Chrome

bool WebGLRenderingContextBase::PaintRenderingResultsToCanvas(
    SourceDrawingBuffer source_buffer) {
  if (isContextLost())
    return false;

  bool must_clear_now = ClearIfComposited() != kSkipped;
  if (!marked_canvas_dirty_ && !must_clear_now)
    return false;

  canvas()->ClearCopiedImage();
  marked_canvas_dirty_ = false;

  if (!canvas()->TryCreateImageBuffer())
    return false;

  ScopedTexture2DRestorer restorer(this);
  ScopedFramebufferRestorer fbo_restorer(this);

  GetDrawingBuffer()->ResolveAndBindForReadAndDraw();
  if (!CopyRenderingResultsFromDrawingBuffer(canvas()->WebGLBuffer(),
                                             source_buffer)) {
    NOTREACHED();
    return false;
  }

  return true;
}

Project: Chrome

void WebGLRenderingContextBase::TexImageBitmapByGPU(
    ImageBitmap* bitmap,
    GLenum target,
    GLuint target_texture,
    GLint xoffset,
    GLint yoffset,
    const IntRect& source_sub_rect) {
  bitmap->BitmapImage()->CopyToTexture(
      GetDrawingBuffer()->ContextProvider()->ContextGL(), target,
      target_texture, true /* unpack_premultiply_alpha */,
      false /* unpack_flip_y_ */, IntPoint(xoffset, yoffset), source_sub_rect);
}

Project: Chrome

void WebGLRenderingContextBase::TexImageByGPU(
    TexImageFunctionID function_id,
    WebGLTexture* texture,
    GLenum target,
    GLint level,
    GLint xoffset,
    GLint yoffset,
    GLint zoffset,
    CanvasImageSource* image,
    const IntRect& source_sub_rectangle) {
  DCHECK(image->IsCanvasElement() || image->IsImageBitmap());
  int width = source_sub_rectangle.Width();
  int height = source_sub_rectangle.Height();

  ScopedTexture2DRestorer restorer(this);

  GLuint target_texture = texture->Object();
  bool possible_direct_copy = false;
  if (function_id == kTexImage2D || function_id == kTexSubImage2D) {
    possible_direct_copy = Extensions3DUtil::CanUseCopyTextureCHROMIUM(target);
  }

  GLint copy_x_offset = xoffset;
  GLint copy_y_offset = yoffset;
  GLenum copy_target = target;

  if (!possible_direct_copy) {
    ContextGL()->GenTextures(1, &target_texture);
    ContextGL()->BindTexture(GL_TEXTURE_2D, target_texture);
    ContextGL()->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
                               GL_NEAREST);
    ContextGL()->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                               GL_NEAREST);
    ContextGL()->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
                               GL_CLAMP_TO_EDGE);
    ContextGL()->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
                               GL_CLAMP_TO_EDGE);
    ContextGL()->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
                            GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    copy_x_offset = 0;
    copy_y_offset = 0;
    copy_target = GL_TEXTURE_2D;
  }

  {
    ScopedUnpackParametersResetRestore temporaryResetUnpack(this);
    if (image->IsCanvasElement()) {
      TexImageCanvasByGPU(function_id, static_cast<HTMLCanvasElement*>(image),
                          copy_target, target_texture, copy_x_offset,
                          copy_y_offset, source_sub_rectangle);
    } else {
      TexImageBitmapByGPU(static_cast<ImageBitmap*>(image), copy_target,
                          target_texture, copy_x_offset, copy_y_offset,
                          source_sub_rectangle);
    }
  }

  if (!possible_direct_copy) {
    GLuint tmp_fbo;
    ContextGL()->GenFramebuffers(1, &tmp_fbo);
    ContextGL()->BindFramebuffer(GL_FRAMEBUFFER, tmp_fbo);
    ContextGL()->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                      GL_TEXTURE_2D, target_texture, 0);
    ContextGL()->BindTexture(texture->GetTarget(), texture->Object());
    if (function_id == kTexImage2D) {
      ContextGL()->CopyTexSubImage2D(target, level, 0, 0, 0, 0, width, height);
    } else if (function_id == kTexSubImage2D) {
      ContextGL()->CopyTexSubImage2D(target, level, xoffset, yoffset, 0, 0,
                                     width, height);
    } else if (function_id == kTexSubImage3D) {
      ContextGL()->CopyTexSubImage3D(target, level, xoffset, yoffset, zoffset,
                                     0, 0, width, height);
    }
    ContextGL()->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                      GL_TEXTURE_2D, 0, 0);
    RestoreCurrentFramebuffer();
    ContextGL()->DeleteFramebuffers(1, &tmp_fbo);
    ContextGL()->DeleteTextures(1, &target_texture);
  }
}

Project: Chrome

void WebGLRenderingContextBase::TexImageCanvasByGPU(
    TexImageFunctionID function_id,
    HTMLCanvasElement* canvas,
    GLenum target,
    GLuint target_texture,
    GLint xoffset,
    GLint yoffset,
    const IntRect& source_sub_rectangle) {
  if (!canvas->Is3d()) {
    if (Extensions3DUtil::CanUseCopyTextureCHROMIUM(target) &&
        canvas->TryCreateImageBuffer()) {
      scoped_refptr<StaticBitmapImage> image =
          canvas->Canvas2DBuffer()->NewImageSnapshot(kPreferAcceleration);
      if (!!image && image->CopyToTexture(
                         ContextGL(), target, target_texture,
                         unpack_premultiply_alpha_, unpack_flip_y_,
                         IntPoint(xoffset, yoffset), source_sub_rectangle)) {
        return;
      }
    }
    NOTREACHED();
  } else {
    WebGLRenderingContextBase* gl =
        ToWebGLRenderingContextBase(canvas->RenderingContext());
    ScopedTexture2DRestorer restorer(gl);
    if (!gl->GetDrawingBuffer()->CopyToPlatformTexture(
            ContextGL(), target, target_texture, unpack_premultiply_alpha_,
            !unpack_flip_y_, IntPoint(xoffset, yoffset), source_sub_rectangle,
            kBackBuffer)) {
      NOTREACHED();
    }
  }
}

Project: Chrome

void WebGLRenderingContextBase::TexImageHelperHTMLCanvasElement(
    const SecurityOrigin* security_origin,
    TexImageFunctionID function_id,
    GLenum target,
    GLint level,
    GLint internalformat,
    GLenum format,
    GLenum type,
    GLint xoffset,
    GLint yoffset,
    GLint zoffset,
    HTMLCanvasElement* canvas,
    const IntRect& source_sub_rectangle,
    GLsizei depth,
    GLint unpack_image_height,
    ExceptionState& exception_state) {
  const char* func_name = GetTexImageFunctionName(function_id);
  if (isContextLost())
    return;

  if (!ValidateHTMLCanvasElement(security_origin, func_name, canvas,
                                 exception_state))
    return;
  WebGLTexture* texture =
      ValidateTexImageBinding(func_name, function_id, target);
  if (!texture)
    return;
  TexImageFunctionType function_type;
  if (function_id == kTexImage2D)
    function_type = kTexImage;
  else
    function_type = kTexSubImage;
  if (!ValidateTexFunc(func_name, function_type, kSourceHTMLCanvasElement,
                       target, level, internalformat, canvas->width(),
                       canvas->height(), depth, 0, format, type, xoffset,
                       yoffset, zoffset))
    return;

  bool selecting_sub_rectangle = false;
  if (!ValidateTexImageSubRectangle(
          func_name, function_id, canvas, source_sub_rectangle, depth,
          unpack_image_height, &selecting_sub_rectangle)) {
    return;
  }

  if (function_id == kTexImage2D || function_id == kTexSubImage2D) {
    if (!canvas->IsAccelerated() || !CanUseTexImageByGPU(format, type)) {
      TexImageImpl(function_id, target, level, internalformat, xoffset, yoffset,
                   zoffset, format, type,
                   canvas->CopiedImage(kBackBuffer, kPreferAcceleration).get(),
                   WebGLImageConversion::kHtmlDomCanvas, unpack_flip_y_,
                   unpack_premultiply_alpha_, source_sub_rectangle, 1, 0);
      return;
    }

    IntRect adjusted_source_sub_rectangle = source_sub_rectangle;
    if (!unpack_flip_y_) {
      adjusted_source_sub_rectangle.SetY(canvas->height() -
                                         adjusted_source_sub_rectangle.MaxY());
    }

    if (function_id == kTexImage2D) {
      TexImage2DBase(target, level, internalformat,
                     source_sub_rectangle.Width(),
                     source_sub_rectangle.Height(), 0, format, type, nullptr);
      TexImageByGPU(function_id, texture, target, level, 0, 0, 0, canvas,
                    adjusted_source_sub_rectangle);
    } else {
      TexImageByGPU(function_id, texture, target, level, xoffset, yoffset, 0,
                    canvas, adjusted_source_sub_rectangle);
    }
  } else {

    TexImageImpl(function_id, target, level, internalformat, xoffset, yoffset,
                 zoffset, format, type,
                 canvas->CopiedImage(kBackBuffer, kPreferAcceleration).get(),
                 WebGLImageConversion::kHtmlDomCanvas, unpack_flip_y_,
                 unpack_premultiply_alpha_, source_sub_rectangle, depth,
                 unpack_image_height);
  }
}

Project: Chrome

void WebGLRenderingContextBase::TexImageHelperHTMLVideoElement(
    const SecurityOrigin* security_origin,
    TexImageFunctionID function_id,
    GLenum target,
    GLint level,
    GLint internalformat,
    GLenum format,
    GLenum type,
    GLint xoffset,
    GLint yoffset,
    GLint zoffset,
    HTMLVideoElement* video,
    const IntRect& source_image_rect,
    GLsizei depth,
    GLint unpack_image_height,
    ExceptionState& exception_state) {
  const char* func_name = GetTexImageFunctionName(function_id);
  if (isContextLost())
    return;

  if (!ValidateHTMLVideoElement(security_origin, func_name, video,
                                exception_state))
    return;
  WebGLTexture* texture =
      ValidateTexImageBinding(func_name, function_id, target);
  if (!texture)
    return;
  TexImageFunctionType function_type;
  if (function_id == kTexImage2D || function_id == kTexImage3D)
    function_type = kTexImage;
  else
    function_type = kTexSubImage;
  if (!ValidateTexFunc(func_name, function_type, kSourceHTMLVideoElement,
                       target, level, internalformat, video->videoWidth(),
                       video->videoHeight(), 1, 0, format, type, xoffset,
                       yoffset, zoffset))
    return;

  WebMediaPlayer::VideoFrameUploadMetadata frame_metadata = {};
  int already_uploaded_id = -1;
  WebMediaPlayer::VideoFrameUploadMetadata* frame_metadata_ptr = nullptr;
  if (RuntimeEnabledFeatures::ExperimentalCanvasFeaturesEnabled()) {
    already_uploaded_id = texture->GetLastUploadedVideoFrameId();
    frame_metadata_ptr = &frame_metadata;
  }

  if (!source_image_rect.IsValid()) {
    SynthesizeGLError(GL_INVALID_OPERATION, func_name,
                      "source sub-rectangle specified via pixel unpack "
                      "parameters is invalid");
    return;
  }
  bool source_image_rect_is_default =
      source_image_rect == SentinelEmptyRect() ||
      source_image_rect ==
          IntRect(0, 0, video->videoWidth(), video->videoHeight());
  const bool use_copyTextureCHROMIUM = function_id == kTexImage2D &&
                                       source_image_rect_is_default &&
                                       depth == 1 && GL_TEXTURE_2D == target &&
                                       CanUseTexImageByGPU(format, type);
  if (use_copyTextureCHROMIUM) {
    DCHECK_EQ(xoffset, 0);
    DCHECK_EQ(yoffset, 0);
    DCHECK_EQ(zoffset, 0);

    if (video->CopyVideoTextureToPlatformTexture(
            ContextGL(), target, texture->Object(), internalformat, format,
            type, level, unpack_premultiply_alpha_, unpack_flip_y_,
            already_uploaded_id, frame_metadata_ptr)) {
      texture->UpdateLastUploadedFrame(frame_metadata);
      return;
    }
  }

  if (source_image_rect_is_default) {
    ScopedUnpackParametersResetRestore(
        this, unpack_flip_y_ || unpack_premultiply_alpha_);
    if (video->TexImageImpl(
            static_cast<WebMediaPlayer::TexImageFunctionID>(function_id),
            target, ContextGL(), texture->Object(), level,
            ConvertTexInternalFormat(internalformat, type), format, type,
            xoffset, yoffset, zoffset, unpack_flip_y_,
            unpack_premultiply_alpha_ &&
                unpack_colorspace_conversion_ == GL_NONE)) {
      texture->ClearLastUploadedFrame();
      return;
    }
  }

  if (use_copyTextureCHROMIUM) {
    std::unique_ptr<AcceleratedImageBufferSurface> surface =
        std::make_unique<AcceleratedImageBufferSurface>(
            IntSize(video->videoWidth(), video->videoHeight()));
    if (surface->IsValid()) {
      video->PaintCurrentFrame(
          surface->Canvas(),
          IntRect(0, 0, video->videoWidth(), video->videoHeight()), nullptr,
          already_uploaded_id, frame_metadata_ptr);


      TexImage2DBase(target, level, internalformat, video->videoWidth(),
                     video->videoHeight(), 0, format, type, nullptr);

      if (Extensions3DUtil::CanUseCopyTextureCHROMIUM(target)) {
        scoped_refptr<StaticBitmapImage> image =
            surface->NewImageSnapshot(kPreferAcceleration);
        if (!!image &&
            image->CopyToTexture(
                ContextGL(), target, texture->Object(),
                unpack_premultiply_alpha_, unpack_flip_y_, IntPoint(0, 0),
                IntRect(0, 0, video->videoWidth(), video->videoHeight()))) {
          texture->UpdateLastUploadedFrame(frame_metadata);
          return;
        }
      }
    }
  }

  scoped_refptr<Image> image =
      VideoFrameToImage(video, already_uploaded_id, frame_metadata_ptr);
  if (!image)
    return;
  TexImageImpl(function_id, target, level, internalformat, xoffset, yoffset,
               zoffset, format, type, image.get(),
               WebGLImageConversion::kHtmlDomVideo, unpack_flip_y_,
               unpack_premultiply_alpha_, source_image_rect, depth,
               unpack_image_height);
  texture->UpdateLastUploadedFrame(frame_metadata);
}

Project: Chrome

void WebGLRenderingContextBase::TexImageHelperImageBitmap(
    TexImageFunctionID function_id,
    GLenum target,
    GLint level,
    GLint internalformat,
    GLenum format,
    GLenum type,
    GLint xoffset,
    GLint yoffset,
    GLint zoffset,
    ImageBitmap* bitmap,
    const IntRect& source_sub_rect,
    GLsizei depth,
    GLint unpack_image_height,
    ExceptionState& exception_state) {
  const char* func_name = GetTexImageFunctionName(function_id);
  if (isContextLost())
    return;
  if (!ValidateImageBitmap(func_name, bitmap, exception_state))
    return;
  WebGLTexture* texture =
      ValidateTexImageBinding(func_name, function_id, target);
  if (!texture)
    return;

  bool selecting_sub_rectangle = false;
  if (!ValidateTexImageSubRectangle(func_name, function_id, bitmap,
                                    source_sub_rect, depth, unpack_image_height,
                                    &selecting_sub_rectangle)) {
    return;
  }

  TexImageFunctionType function_type;
  if (function_id == kTexImage2D)
    function_type = kTexImage;
  else
    function_type = kTexSubImage;

  GLsizei width = source_sub_rect.Width();
  GLsizei height = source_sub_rect.Height();
  if (!ValidateTexFunc(func_name, function_type, kSourceImageBitmap, target,
                       level, internalformat, width, height, depth, 0, format,
                       type, xoffset, yoffset, zoffset))
    return;
  DCHECK(bitmap->BitmapImage());

  if (function_id != kTexSubImage3D && function_id != kTexImage3D &&
      bitmap->IsAccelerated() && CanUseTexImageByGPU(format, type) &&
      !selecting_sub_rectangle) {
    if (function_id == kTexImage2D) {
      TexImage2DBase(target, level, internalformat, width, height, 0, format,
                     type, nullptr);
      TexImageByGPU(function_id, texture, target, level, 0, 0, 0, bitmap,
                    source_sub_rect);
    } else if (function_id == kTexSubImage2D) {
      TexImageByGPU(function_id, texture, target, level, xoffset, yoffset, 0,
                    bitmap, source_sub_rect);
    }
    return;
  }
  sk_sp<SkImage> sk_image =
      bitmap->BitmapImage()->PaintImageForCurrentFrame().GetSkImage();
  SkPixmap pixmap;
  uint8_t* pixel_data_ptr = nullptr;
  scoped_refptr<Uint8Array> pixel_data;
  bool peek_succeed = sk_image->peekPixels(&pixmap);
  if (peek_succeed) {
    pixel_data_ptr = static_cast<uint8_t*>(pixmap.writable_addr());
  } else {
    pixel_data = bitmap->CopyBitmapData(
        bitmap->IsPremultiplied() ? kPremultiplyAlpha : kUnpremultiplyAlpha);
    pixel_data_ptr = pixel_data->Data();
  }
  Vector<uint8_t> data;
  bool need_conversion = true;
  bool have_peekable_rgba =
      (peek_succeed &&
       pixmap.colorType() == SkColorType::kRGBA_8888_SkColorType);
  bool is_pixel_data_rgba = (have_peekable_rgba || !peek_succeed);
  if (is_pixel_data_rgba && format == GL_RGBA && type == GL_UNSIGNED_BYTE &&
      !selecting_sub_rectangle && depth == 1) {
    need_conversion = false;
  } else {
    if (type == GL_UNSIGNED_INT_10F_11F_11F_REV) {
      type = GL_FLOAT;
    }
    bool is_pixel_data_bgra =
        pixmap.colorType() == SkColorType::kBGRA_8888_SkColorType;
    if ((is_pixel_data_bgra &&
         !WebGLImageConversion::ExtractImageData(
             pixel_data_ptr, WebGLImageConversion::DataFormat::kDataFormatBGRA8,
             bitmap->Size(), source_sub_rect, depth, unpack_image_height,
             format, type, false, false, data)) ||
        (is_pixel_data_rgba &&
         !WebGLImageConversion::ExtractImageData(
             pixel_data_ptr, WebGLImageConversion::DataFormat::kDataFormatRGBA8,
             bitmap->Size(), source_sub_rect, depth, unpack_image_height,
             format, type, false, false, data))) {
      SynthesizeGLError(GL_INVALID_VALUE, func_name, "bad image data");
      return;
    }
  }
  ScopedUnpackParametersResetRestore temporary_reset_unpack(this);
  if (function_id == kTexImage2D) {
    TexImage2DBase(target, level, internalformat, width, height, 0, format,
                   type, need_conversion ? data.data() : pixel_data_ptr);
  } else if (function_id == kTexSubImage2D) {
    ContextGL()->TexSubImage2D(target, level, xoffset, yoffset, width, height,
                               format, type,
                               need_conversion ? data.data() : pixel_data_ptr);
  } else if (function_id == kTexImage3D) {
    ContextGL()->TexImage3D(target, level, internalformat, width, height, depth,
                            0, format, type,
                            need_conversion ? data.data() : pixel_data_ptr);
  } else {
    DCHECK_EQ(function_id, kTexSubImage3D);
    ContextGL()->TexSubImage3D(target, level, xoffset, yoffset, zoffset, width,
                               height, depth, format, type,
                               need_conversion ? data.data() : pixel_data_ptr);
  }
}