Global Metrics

path: .metrics.cyclomatic.sum
old: 4.0
new: 67.0

path: .metrics.cyclomatic.average
old: 1.0
new: 2.2333333333333334

path: .metrics.nexits.sum
old: 0.0
new: 28.0

path: .metrics.nexits.average
old: null
new: 1.037037037037037

path: .metrics.cognitive.sum
old: 0.0
new: 39.0

path: .metrics.cognitive.average
old: null
new: 1.4444444444444444

path: .metrics.nom.total
old: 0.0
new: 27.0

path: .metrics.nom.functions
old: 0.0
new: 27.0

path: .metrics.halstead.N1
old: 110.0
new: 759.0

path: .metrics.halstead.length
old: 205.0
new: 1247.0

path: .metrics.halstead.time
old: 676.6459048155574
new: 18165.03222511953

path: .metrics.halstead.effort
old: 12179.626286680032
new: 326970.5800521515

path: .metrics.halstead.bugs
old: 0.17645530535138337
new: 1.5820414711747477

path: .metrics.halstead.level
old: 0.10350877192982456
new: 0.029751062537947785

path: .metrics.halstead.estimated_program_length
old: 390.0954899210025
new: 1620.8650920049945

path: .metrics.halstead.n1
old: 12.0
new: 27.0

path: .metrics.halstead.purity_ratio
old: 1.902904828882939
new: 1.299811621495585

path: .metrics.halstead.N2
old: 95.0
new: 488.0

path: .metrics.halstead.vocabulary
old: 71.0
new: 223.0

path: .metrics.halstead.volume
old: 1260.6981594984595
new: 9727.72217520062

path: .metrics.halstead.n2
old: 59.0
new: 196.0

path: .metrics.halstead.difficulty
old: 9.66101694915254
new: 33.61224489795919

path: .metrics.mi.mi_original
old: 54.6072442124612
new: 12.665899928768908

path: .metrics.mi.mi_visual_studio
old: 31.934060942959768
new: 7.4069590226718764

path: .metrics.mi.mi_sei
old: 43.13064795538851
new: -33.09065169310504

path: .metrics.loc.cloc
old: 44.0
new: 19.0

path: .metrics.loc.ploc
old: 53.0
new: 278.0

path: .metrics.loc.blank
old: 29.0
new: 59.0

path: .metrics.loc.lloc
old: 0.0
new: 111.0

path: .metrics.loc.sloc
old: 126.0
new: 356.0

path: .metrics.nargs.average
old: null
new: 0.6666666666666666

path: .metrics.nargs.sum
old: 0.0
new: 18.0

Spaces Data

Minimal test - lines (25, 356)

path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 28.0

path: .spaces[0].metrics.nexits.average
old: null
new: 1.037037037037037

path: .spaces[0].metrics.nom.total
old: 0.0
new: 27.0

path: .spaces[0].metrics.nom.functions
old: 0.0
new: 27.0

path: .spaces[0].metrics.cognitive.average
old: null
new: 1.4444444444444444

path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 39.0

path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 2.2758620689655173

path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 66.0

path: .spaces[0].metrics.loc.blank
old: 0.0
new: 56.0

path: .spaces[0].metrics.loc.ploc
old: 1.0
new: 262.0

path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 14.0

path: .spaces[0].metrics.loc.sloc
old: 1.0
new: 332.0

path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 111.0

path: .spaces[0].metrics.halstead.N1
old: 0.0
new: 759.0

path: .spaces[0].metrics.halstead.volume
old: 0.0
new: 9486.941732749823

path: .spaces[0].metrics.halstead.level
old: null
new: 0.02834547020593532

path: .spaces[0].metrics.halstead.bugs
old: 0.0
new: 1.606844277071659

path: .spaces[0].metrics.halstead.N2
old: 1.0
new: 473.0

path: .spaces[0].metrics.halstead.effort
old: 0.0
new: 334689.8698037238

path: .spaces[0].metrics.halstead.n1
old: 0.0
new: 27.0

path: .spaces[0].metrics.halstead.purity_ratio
old: null
new: 1.2060503799679172

path: .spaces[0].metrics.halstead.time
old: 0.0
new: 18593.88165576243

path: .spaces[0].metrics.halstead.length
old: 1.0
new: 1232.0

path: .spaces[0].metrics.halstead.estimated_program_length
old: null
new: 1485.854068120474

path: .spaces[0].metrics.halstead.vocabulary
old: 1.0
new: 208.0

path: .spaces[0].metrics.halstead.difficulty
old: 0.0
new: 35.27900552486188

path: .spaces[0].metrics.halstead.n2
old: 1.0
new: 181.0

path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 18.0

path: .spaces[0].metrics.nargs.average
old: null
new: 0.6666666666666666

path: .spaces[0].metrics.mi.mi_original
old: null
new: 14.156921300164484

path: .spaces[0].metrics.mi.mi_sei
old: null
new: -32.917214194979785

path: .spaces[0].metrics.mi.mi_visual_studio
old: null
new: 8.278901345125428

Code

namespace mozilla {
namespace widget {

using namespace mozilla::gfx;
using namespace mozilla;

/* static */
RefPtr CompositorWidget::CreateLocal(
    const CompositorWidgetInitData& aInitData,
    const layers::CompositorOptions& aOptions, nsIWidget* aWidget) {
  if (aInitData.type() ==
      CompositorWidgetInitData::THeadlessCompositorWidgetInitData) {
    return new HeadlessCompositorWidget(
        aInitData.get_HeadlessCompositorWidgetInitData(), aOptions,
        static_cast(aWidget));
  } else {
    return new InProcessWinCompositorWidget(
        aInitData.get_WinCompositorWidgetInitData(), aOptions,
        static_cast(aWidget));
  }
}

InProcessWinCompositorWidget::InProcessWinCompositorWidget(
    const WinCompositorWidgetInitData& aInitData,
    const layers::CompositorOptions& aOptions, nsWindow* aWindow)
    : WinCompositorWidget(aInitData, aOptions),
      mWindow(aWindow),
      mWnd(reinterpret_cast(aInitData.hWnd())),
      mTransparentSurfaceLock("mTransparentSurfaceLock"),
      mTransparencyMode(aInitData.transparencyMode()),
      mMemoryDC(nullptr),
      mCompositeDC(nullptr),
      mLockedBackBufferData(nullptr) {
  MOZ_ASSERT(mWindow);
  MOZ_ASSERT(mWnd && ::IsWindow(mWnd));

  // mNotDeferEndRemoteDrawing is set on the main thread during init,
  // but is only accessed after on the compositor thread.
  mNotDeferEndRemoteDrawing =
      StaticPrefs::layers_offmainthreadcomposition_frame_rate() == 0 ||
      gfxPlatform::IsInLayoutAsapMode() || gfxPlatform::ForceSoftwareVsync();
}

void InProcessWinCompositorWidget::OnDestroyWindow() {
  EnterPresentLock();
  MutexAutoLock lock(mTransparentSurfaceLock);
  mTransparentSurface = nullptr;
  mMemoryDC = nullptr;
  LeavePresentLock();
}

bool InProcessWinCompositorWidget::OnWindowResize(
    const LayoutDeviceIntSize& aSize) {
  return true;
}

void InProcessWinCompositorWidget::OnWindowModeChange(nsSizeMode aSizeMode) {}

bool InProcessWinCompositorWidget::PreRender(WidgetRenderingContext* aContext) {
  // This can block waiting for WM_SETTEXT to finish
  // Using PreRender is unnecessarily pessimistic because
  // we technically only need to block during the present call
  // not all of compositor rendering
  mPresentLock.Enter();
  return true;
}

void InProcessWinCompositorWidget::PostRender(
    WidgetRenderingContext* aContext) {
  mPresentLock.Leave();
}

LayoutDeviceIntSize InProcessWinCompositorWidget::GetClientSize() {
  RECT r;
  if (!::GetClientRect(mWnd, &r)) {
    return LayoutDeviceIntSize();
  }
  return LayoutDeviceIntSize(r.right - r.left, r.bottom - r.top);
}

already_AddRefed
InProcessWinCompositorWidget::StartRemoteDrawing() {
  MutexAutoLock lock(mTransparentSurfaceLock);

  MOZ_ASSERT(!mCompositeDC);

  RefPtr surf;
  if (mTransparencyMode == eTransparencyTransparent) {
    surf = EnsureTransparentSurface();
  }

  // Must call this after EnsureTransparentSurface(), since it could update
  // the DC.
  HDC dc = GetWindowSurface();
  if (!surf) {
    if (!dc) {
      return nullptr;
    }
    uint32_t flags = (mTransparencyMode == eTransparencyOpaque)
                         ? 0
                         : gfxWindowsSurface::FLAG_IS_TRANSPARENT;
    surf = new gfxWindowsSurface(dc, flags);
  }

  IntSize size = surf->GetSize();
  if (size.width <= 0 || size.height <= 0) {
    if (dc) {
      FreeWindowSurface(dc);
    }
    return nullptr;
  }

  RefPtr dt =
      mozilla::gfx::Factory::CreateDrawTargetForCairoSurface(
          surf->CairoSurface(), size);
  if (dt) {
    mCompositeDC = dc;
  } else {
    FreeWindowSurface(dc);
  }

  return dt.forget();
}

void InProcessWinCompositorWidget::EndRemoteDrawing() {
  MOZ_ASSERT(!mLockedBackBufferData);

  if (mTransparencyMode == eTransparencyTransparent) {
    MOZ_ASSERT(mTransparentSurface);
    RedrawTransparentWindow();
  }
  if (mCompositeDC) {
    FreeWindowSurface(mCompositeDC);
  }
  mCompositeDC = nullptr;
}

bool InProcessWinCompositorWidget::NeedsToDeferEndRemoteDrawing() {
  if (mNotDeferEndRemoteDrawing) {
    return false;
  }

  IDirectDraw7* ddraw = DeviceManagerDx::Get()->GetDirectDraw();
  if (!ddraw) {
    return false;
  }

  DWORD scanLine = 0;
  int height = ::GetSystemMetrics(SM_CYSCREEN);
  HRESULT ret = ddraw->GetScanLine(&scanLine);
  if (ret == DDERR_VERTICALBLANKINPROGRESS) {
    scanLine = 0;
  } else if (ret != DD_OK) {
    return false;
  }

  // Check if there is a risk of tearing with GDI.
  if (static_cast(scanLine) > height / 2) {
    // No need to defer.
    return false;
  }

  return true;
}

already_AddRefed
InProcessWinCompositorWidget::GetBackBufferDrawTarget(
    gfx::DrawTarget* aScreenTarget, const gfx::IntRect& aRect,
    bool* aOutIsCleared) {
  MOZ_ASSERT(!mLockedBackBufferData);

  RefPtr target = CompositorWidget::GetBackBufferDrawTarget(
      aScreenTarget, aRect, aOutIsCleared);
  if (!target) {
    return nullptr;
  }

  MOZ_ASSERT(target->GetBackendType() == BackendType::CAIRO);

  uint8_t* destData;
  IntSize destSize;
  int32_t destStride;
  SurfaceFormat destFormat;
  if (!target->LockBits(&destData, &destSize, &destStride, &destFormat)) {
    // LockBits is not supported. Use original DrawTarget.
    return target.forget();
  }

  RefPtr dataTarget = Factory::CreateDrawTargetForData(
      BackendType::CAIRO, destData, destSize, destStride, destFormat);
  mLockedBackBufferData = destData;

  return dataTarget.forget();
}

already_AddRefed
InProcessWinCompositorWidget::EndBackBufferDrawing() {
  if (mLockedBackBufferData) {
    MOZ_ASSERT(mLastBackBuffer);
    mLastBackBuffer->ReleaseBits(mLockedBackBufferData);
    mLockedBackBufferData = nullptr;
  }
  return CompositorWidget::EndBackBufferDrawing();
}

bool InProcessWinCompositorWidget::InitCompositor(
    layers::Compositor* aCompositor) {
  if (aCompositor->GetBackendType() == layers::LayersBackend::LAYERS_BASIC) {
    DeviceManagerDx::Get()->InitializeDirectDraw();
  }
  return true;
}

void InProcessWinCompositorWidget::EnterPresentLock() { mPresentLock.Enter(); }

void InProcessWinCompositorWidget::LeavePresentLock() { mPresentLock.Leave(); }

RefPtr InProcessWinCompositorWidget::EnsureTransparentSurface() {
  mTransparentSurfaceLock.AssertCurrentThreadOwns();
  MOZ_ASSERT(mTransparencyMode == eTransparencyTransparent);

  IntSize size = GetClientSize().ToUnknownSize();
  if (!mTransparentSurface || mTransparentSurface->GetSize() != size) {
    mTransparentSurface = nullptr;
    mMemoryDC = nullptr;
    CreateTransparentSurface(size);
  }

  RefPtr surface = mTransparentSurface;
  return surface.forget();
}

void InProcessWinCompositorWidget::CreateTransparentSurface(
    const gfx::IntSize& aSize) {
  mTransparentSurfaceLock.AssertCurrentThreadOwns();
  MOZ_ASSERT(!mTransparentSurface && !mMemoryDC);
  RefPtr surface =
      new gfxWindowsSurface(aSize, SurfaceFormat::A8R8G8B8_UINT32);
  mTransparentSurface = surface;
  mMemoryDC = surface->GetDC();
}

void InProcessWinCompositorWidget::UpdateTransparency(
    nsTransparencyMode aMode) {
  EnterPresentLock();
  MutexAutoLock lock(mTransparentSurfaceLock);
  if (mTransparencyMode == aMode) {
    return;
  }

  mTransparencyMode = aMode;
  mTransparentSurface = nullptr;
  mMemoryDC = nullptr;

  if (mTransparencyMode == eTransparencyTransparent) {
    EnsureTransparentSurface();
  }
  LeavePresentLock();
}

bool InProcessWinCompositorWidget::HasGlass() const {
  MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread() ||
             wr::RenderThread::IsInRenderThread());

  nsTransparencyMode transparencyMode = mTransparencyMode;
  return transparencyMode == eTransparencyGlass ||
         transparencyMode == eTransparencyBorderlessGlass;
}

void InProcessWinCompositorWidget::ClearTransparentWindow() {
  EnterPresentLock();
  MutexAutoLock lock(mTransparentSurfaceLock);
  if (!mTransparentSurface) {
    return;
  }

  EnsureTransparentSurface();

  IntSize size = mTransparentSurface->GetSize();
  if (!size.IsEmpty()) {
    RefPtr drawTarget =
        gfxPlatform::CreateDrawTargetForSurface(mTransparentSurface, size);
    if (!drawTarget) {
      return;
    }
    drawTarget->ClearRect(Rect(0, 0, size.width, size.height));
    RedrawTransparentWindow();
  }
  LeavePresentLock();
}

bool InProcessWinCompositorWidget::RedrawTransparentWindow() {
  MOZ_ASSERT(mTransparencyMode == eTransparencyTransparent);

  LayoutDeviceIntSize size = GetClientSize();

  ::GdiFlush();

  BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
  SIZE winSize = {size.width, size.height};
  POINT srcPos = {0, 0};
  HWND hWnd = WinUtils::GetTopLevelHWND(mWnd, true);
  RECT winRect;
  ::GetWindowRect(hWnd, &winRect);

  // perform the alpha blend
  return !!::UpdateLayeredWindow(hWnd, nullptr, (POINT*)&winRect, &winSize,
                                 mMemoryDC, &srcPos, 0, &bf, ULW_ALPHA);
}

HDC InProcessWinCompositorWidget::GetWindowSurface() {
  return eTransparencyTransparent == mTransparencyMode ? mMemoryDC
                                                       : ::GetDC(mWnd);
}

void InProcessWinCompositorWidget::FreeWindowSurface(HDC dc) {
  if (eTransparencyTransparent != mTransparencyMode) ::ReleaseDC(mWnd, dc);
}

bool InProcessWinCompositorWidget::IsHidden() const { return ::IsIconic(mWnd); }

nsIWidget* InProcessWinCompositorWidget::RealWidget() { return mWindow; }

void InProcessWinCompositorWidget::ObserveVsync(VsyncObserver* aObserver) {
  if (RefPtr cvd =
          mWindow->GetCompositorVsyncDispatcher()) {
    cvd->SetCompositorVsyncObserver(aObserver);
  }
}

}  // namespace widget
}  // namespace mozilla