<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <oleacc.h>

#include "app/l10n_util.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/view_ids.h"
#include "chrome/browser/views/bookmark_bar_view.h"
#include "chrome/browser/views/frame/browser_view.h"
#include "chrome/browser/views/toolbar_view.h"
#include "chrome/test/in_process_browser_test.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "views/accessibility/view_accessibility_wrapper.h"
#include "views/widget/root_view.h"
#include "views/widget/widget_win.h"
#include "views/window/window.h"

namespace {

VARIANT id_self = {VT_I4, CHILDID_SELF};

// Dummy class to force creation of ATL module, needed by COM to instantiate
// ViewAccessibility.
class TestAtlModule : public CAtlDllModuleT< TestAtlModule > {};
TestAtlModule test_atl_module_;

class BrowserViewsAccessibilityTest : public InProcessBrowserTest {
 public:
  BrowserViewsAccessibilityTest() {
    ::CoInitialize(NULL);
  }

  ~BrowserViewsAccessibilityTest() {
    ::CoUninitialize();
  }

  // Retrieves an instance of BrowserWindowTesting
  BrowserWindowTesting* GetBrowserWindowTesting() {
    BrowserWindow* browser_window = browser()->window();

    if (!browser_window)
      return NULL;

    return browser_window->GetBrowserWindowTesting();
  }

  // Retrieve an instance of BrowserView
  BrowserView* GetBrowserView() {
    return BrowserView::GetBrowserViewForNativeWindow(
               browser()->window()->GetNativeHandle());
  }

  // Retrieves and initializes an instance of LocationBarView.
  LocationBarView* GetLocationBarView() {
    BrowserWindowTesting* browser_window_testing = GetBrowserWindowTesting();

    if (!browser_window_testing)
      return NULL;

    return GetBrowserWindowTesting()->GetLocationBarView();
  }

  // Retrieves and initializes an instance of ToolbarView.
  ToolbarView* GetToolbarView() {
    BrowserWindowTesting* browser_window_testing = GetBrowserWindowTesting();

    if (!browser_window_testing)
      return NULL;

    return browser_window_testing->GetToolbarView();
  }

  // Retrieves and initializes an instance of BookmarkBarView.
  BookmarkBarView* GetBookmarkBarView() {
    BrowserWindowTesting* browser_window_testing = GetBrowserWindowTesting();

    if (!browser_window_testing)
      return NULL;

    return browser_window_testing->GetBookmarkBarView();
  }

  // Retrieves and verifies the accessibility object for the given View.
  void TestViewAccessibilityObject(views::View* view, std::wstring name,
                                   int32 role) {
    ASSERT_TRUE(NULL != view);

    IAccessible* acc_obj = NULL;
    HRESULT hr = view->GetViewAccessibilityWrapper()->GetInstance(
        IID_IAccessible, reinterpret_cast<void**>(&acc_obj));
    ASSERT_EQ(S_OK, hr);
    ASSERT_TRUE(NULL != acc_obj);

    TestAccessibilityInfo(acc_obj, name, role);
  }

  // Verifies MSAA Name and Role properties of the given IAccessible.
  void TestAccessibilityInfo(IAccessible* acc_obj, std::wstring name,
                             int32 role) {
    // Verify MSAA Name property.
    BSTR acc_name;

    HRESULT hr = acc_obj->get_accName(id_self, &acc_name);
    ASSERT_EQ(S_OK, hr);
    EXPECT_STREQ(acc_name, name.c_str());

    // Verify MSAA Role property.
    VARIANT acc_role;
    ::VariantInit(&acc_role);

    hr = acc_obj->get_accRole(id_self, &acc_role);
    ASSERT_EQ(S_OK, hr);
    EXPECT_EQ(VT_I4, acc_role.vt);
    EXPECT_EQ(role, acc_role.lVal);

    ::VariantClear(&acc_role);
    ::SysFreeString(acc_name);
  }
};

// Retrieve accessibility object for main window and verify accessibility info.
// Fails, http://crbug.com/44486.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest,
                       FAILS_TestChromeWindowAccObj) {
  BrowserWindow* browser_window = browser()->window();
  ASSERT_TRUE(NULL != browser_window);

  HWND hwnd = browser_window->GetNativeHandle();
  ASSERT_TRUE(NULL != hwnd);

  // Get accessibility object.
  IAccessible* acc_obj = NULL;
  HRESULT hr = ::AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible,
                                            reinterpret_cast<void**>(&acc_obj));
  ASSERT_EQ(S_OK, hr);
  ASSERT_TRUE(NULL != acc_obj);

  // TODO(ctguil): Fix. The window title could be "New Tab - Chromium" or
  // "about:blank - Chromium"
  TestAccessibilityInfo(acc_obj, l10n_util::GetString(IDS_PRODUCT_NAME),
                        ROLE_SYSTEM_WINDOW);

  acc_obj->Release();
}

// Retrieve accessibility object for non client view and verify accessibility
// info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestNonClientViewAccObj) {
  views::View* non_client_view =
  GetBrowserView()->GetWindow()->GetNonClientView();

  TestViewAccessibilityObject(non_client_view,
      l10n_util::GetString(IDS_PRODUCT_NAME),
      ROLE_SYSTEM_WINDOW);
}

// Retrieve accessibility object for browser root view and verify
// accessibility info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest,
                       TestBrowserRootViewAccObj) {
  views::View* browser_root_view =
      GetBrowserView()->frame()->GetFrameView()->GetRootView();

  TestViewAccessibilityObject(browser_root_view,
                              l10n_util::GetString(IDS_PRODUCT_NAME),
                              ROLE_SYSTEM_APPLICATION);
}

// Retrieve accessibility object for browser view and verify accessibility info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestBrowserViewAccObj) {
  // Verify root view MSAA name and role.
  TestViewAccessibilityObject(GetBrowserView(),
                              l10n_util::GetString(IDS_PRODUCT_NAME),
                              ROLE_SYSTEM_CLIENT);
}

// Retrieve accessibility object for toolbar view and verify accessibility info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestToolbarViewAccObj) {
  // Verify toolbar MSAA name and role.
  TestViewAccessibilityObject(GetToolbarView(),
                              l10n_util::GetString(IDS_ACCNAME_TOOLBAR),
                              ROLE_SYSTEM_TOOLBAR);
}

// Retrieve accessibility object for Back button and verify accessibility info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestBackButtonAccObj) {
  // Verify Back button MSAA name and role.
  TestViewAccessibilityObject(
      GetToolbarView()->GetViewByID(VIEW_ID_BACK_BUTTON),
      l10n_util::GetString(IDS_ACCNAME_BACK), ROLE_SYSTEM_BUTTONDROPDOWN);
}

// Retrieve accessibility object for Forward button and verify accessibility
// info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestForwardButtonAccObj) {
  // Verify Forward button MSAA name and role.
  TestViewAccessibilityObject(
      GetToolbarView()->GetViewByID(VIEW_ID_FORWARD_BUTTON),
      l10n_util::GetString(IDS_ACCNAME_FORWARD), ROLE_SYSTEM_BUTTONDROPDOWN);
}

// Retrieve accessibility object for Reload button and verify accessibility
// info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestReloadButtonAccObj) {
  // Verify Reload button MSAA name and role.
  TestViewAccessibilityObject(
      GetToolbarView()->GetViewByID(VIEW_ID_RELOAD_BUTTON),
      l10n_util::GetString(IDS_ACCNAME_RELOAD), ROLE_SYSTEM_PUSHBUTTON);
}

// Retrieve accessibility object for Home button and verify accessibility info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestHomeButtonAccObj) {
  // Verify Home button MSAA name and role.
  TestViewAccessibilityObject(
      GetToolbarView()->GetViewByID(VIEW_ID_HOME_BUTTON),
      l10n_util::GetString(IDS_ACCNAME_HOME), ROLE_SYSTEM_PUSHBUTTON);
}

// Retrieve accessibility object for Star button and verify accessibility info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest,
                       TestStarButtonAccObj) {
  // Verify Star button MSAA name and role.
  TestViewAccessibilityObject(
      GetToolbarView()->GetViewByID(VIEW_ID_STAR_BUTTON),
      l10n_util::GetString(IDS_ACCNAME_STAR), ROLE_SYSTEM_PUSHBUTTON);
}

// Retrieve accessibility object for location bar view and verify accessibility
// info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest,
                       TestLocationBarViewAccObj) {
  // Verify location bar MSAA name and role.
  TestViewAccessibilityObject(GetLocationBarView(),
                              l10n_util::GetString(IDS_ACCNAME_LOCATION),
                              ROLE_SYSTEM_GROUPING);
}

// Retrieve accessibility object for Go button and verify accessibility info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestGoButtonAccObj) {
  // Verify Go button MSAA name and role.
  TestViewAccessibilityObject(GetToolbarView()->GetViewByID(VIEW_ID_GO_BUTTON),
                              l10n_util::GetString(IDS_ACCNAME_GO),
                              ROLE_SYSTEM_PUSHBUTTON);
}

// Retrieve accessibility object for Page menu button and verify accessibility
// info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestPageMenuAccObj) {
  // Verify Page menu button MSAA name and role.
  TestViewAccessibilityObject(GetToolbarView()->GetViewByID(VIEW_ID_PAGE_MENU),
                              l10n_util::GetString(IDS_ACCNAME_PAGE),
                              ROLE_SYSTEM_BUTTONMENU);
}

// Retrieve accessibility object for App menu button and verify accessibility
// info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestAppMenuAccObj) {
  // Verify App menu button MSAA name and role.
  TestViewAccessibilityObject(GetToolbarView()->GetViewByID(VIEW_ID_APP_MENU),
                              l10n_util::GetString(IDS_ACCNAME_APP),
                              ROLE_SYSTEM_BUTTONMENU);
}

IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest,
                       TestBookmarkBarViewAccObj) {
  TestViewAccessibilityObject(GetBookmarkBarView(),
                              l10n_util::GetString(IDS_ACCNAME_BOOKMARKS),
                              ROLE_SYSTEM_TOOLBAR);
}

// Fails, http://crbug.com/44486.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest,
                       FAILS_TestAboutChromeViewAccObj) {
  //  Firstly, test that the WindowDelegate got updated.
  views::Window* aboutChromeWindow = GetBrowserView()->ShowAboutChromeDialog();
  EXPECT_STREQ(aboutChromeWindow->GetDelegate()->GetWindowTitle().c_str(),
               l10n_util::GetString(IDS_ABOUT_CHROME_TITLE).c_str());
  EXPECT_EQ(aboutChromeWindow->GetDelegate()->accessible_role(),
            AccessibilityTypes::ROLE_DIALOG);

  // Also test the accessibility object directly.
  IAccessible* acc_obj = NULL;
  HRESULT hr =
    ::AccessibleObjectFromWindow(aboutChromeWindow->GetNativeWindow(),
                                 OBJID_CLIENT,
                                 IID_IAccessible,
                                 reinterpret_cast<void**>(&acc_obj));
  ASSERT_EQ(S_OK, hr);
  ASSERT_TRUE(NULL != acc_obj);

  TestAccessibilityInfo(acc_obj, l10n_util::GetString(IDS_ABOUT_CHROME_TITLE),
                        ROLE_SYSTEM_DIALOG);

  acc_obj->Release();
}
}  // Namespace.

<commit_msg>Fix flakyness of browser_tests:BrowserViewsAccessibilityTest.TestChromeWindowAccObj<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <oleacc.h>

#include "app/l10n_util.h"
#include "base/scoped_comptr_win.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/view_ids.h"
#include "chrome/browser/views/bookmark_bar_view.h"
#include "chrome/browser/views/frame/browser_view.h"
#include "chrome/browser/views/toolbar_view.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "views/accessibility/view_accessibility_wrapper.h"
#include "views/widget/root_view.h"
#include "views/widget/widget_win.h"
#include "views/window/window.h"

namespace {

VARIANT id_self = {VT_I4, CHILDID_SELF};

// Dummy class to force creation of ATL module, needed by COM to instantiate
// ViewAccessibility.
class TestAtlModule : public CAtlDllModuleT< TestAtlModule > {};
TestAtlModule test_atl_module_;

class BrowserViewsAccessibilityTest : public InProcessBrowserTest {
 public:
  BrowserViewsAccessibilityTest() {
    ::CoInitialize(NULL);
  }

  ~BrowserViewsAccessibilityTest() {
    ::CoUninitialize();
  }

  // Retrieves an instance of BrowserWindowTesting
  BrowserWindowTesting* GetBrowserWindowTesting() {
    BrowserWindow* browser_window = browser()->window();

    if (!browser_window)
      return NULL;

    return browser_window->GetBrowserWindowTesting();
  }

  // Retrieve an instance of BrowserView
  BrowserView* GetBrowserView() {
    return BrowserView::GetBrowserViewForNativeWindow(
               browser()->window()->GetNativeHandle());
  }

  // Retrieves and initializes an instance of LocationBarView.
  LocationBarView* GetLocationBarView() {
    BrowserWindowTesting* browser_window_testing = GetBrowserWindowTesting();

    if (!browser_window_testing)
      return NULL;

    return GetBrowserWindowTesting()->GetLocationBarView();
  }

  // Retrieves and initializes an instance of ToolbarView.
  ToolbarView* GetToolbarView() {
    BrowserWindowTesting* browser_window_testing = GetBrowserWindowTesting();

    if (!browser_window_testing)
      return NULL;

    return browser_window_testing->GetToolbarView();
  }

  // Retrieves and initializes an instance of BookmarkBarView.
  BookmarkBarView* GetBookmarkBarView() {
    BrowserWindowTesting* browser_window_testing = GetBrowserWindowTesting();

    if (!browser_window_testing)
      return NULL;

    return browser_window_testing->GetBookmarkBarView();
  }

  // Retrieves and verifies the accessibility object for the given View.
  void TestViewAccessibilityObject(views::View* view, std::wstring name,
                                   int32 role) {
    ASSERT_TRUE(NULL != view);

    IAccessible* acc_obj = NULL;
    HRESULT hr = view->GetViewAccessibilityWrapper()->GetInstance(
        IID_IAccessible, reinterpret_cast<void**>(&acc_obj));
    ASSERT_EQ(S_OK, hr);
    ASSERT_TRUE(NULL != acc_obj);

    TestAccessibilityInfo(acc_obj, name, role);
  }

  // Verifies MSAA Name and Role properties of the given IAccessible.
  void TestAccessibilityInfo(IAccessible* acc_obj, std::wstring name,
                             int32 role) {
    // Verify MSAA Name property.
    BSTR acc_name;

    HRESULT hr = acc_obj->get_accName(id_self, &acc_name);
    ASSERT_EQ(S_OK, hr);
    EXPECT_STREQ(acc_name, name.c_str());

    // Verify MSAA Role property.
    VARIANT acc_role;
    ::VariantInit(&acc_role);

    hr = acc_obj->get_accRole(id_self, &acc_role);
    ASSERT_EQ(S_OK, hr);
    EXPECT_EQ(VT_I4, acc_role.vt);
    EXPECT_EQ(role, acc_role.lVal);

    ::VariantClear(&acc_role);
    ::SysFreeString(acc_name);
  }
};

// Retrieve accessibility object for main window and verify accessibility info.
// Fails, http://crbug.com/44486.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest,
                       FAILS_TestChromeWindowAccObj) {
  BrowserWindow* browser_window = browser()->window();
  ASSERT_TRUE(NULL != browser_window);

  HWND hwnd = browser_window->GetNativeHandle();
  ASSERT_TRUE(NULL != hwnd);

  // Get accessibility object.
  ScopedComPtr<IAccessible> acc_obj;
  HRESULT hr = ::AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible,
                                            reinterpret_cast<void**>(&acc_obj));
  ASSERT_EQ(S_OK, hr);
  ASSERT_TRUE(NULL != acc_obj);

  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kAboutBlankURL));
  std::wstring title =
      l10n_util::GetStringF(IDS_BROWSER_WINDOW_TITLE_FORMAT,
      ASCIIToWide(chrome::kAboutBlankURL));
  TestAccessibilityInfo(acc_obj, title, ROLE_SYSTEM_WINDOW);
}

// Retrieve accessibility object for non client view and verify accessibility
// info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestNonClientViewAccObj) {
  views::View* non_client_view =
  GetBrowserView()->GetWindow()->GetNonClientView();

  TestViewAccessibilityObject(non_client_view,
      l10n_util::GetString(IDS_PRODUCT_NAME),
      ROLE_SYSTEM_WINDOW);
}

// Retrieve accessibility object for browser root view and verify
// accessibility info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest,
                       TestBrowserRootViewAccObj) {
  views::View* browser_root_view =
      GetBrowserView()->frame()->GetFrameView()->GetRootView();

  TestViewAccessibilityObject(browser_root_view,
                              l10n_util::GetString(IDS_PRODUCT_NAME),
                              ROLE_SYSTEM_APPLICATION);
}

// Retrieve accessibility object for browser view and verify accessibility info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestBrowserViewAccObj) {
  // Verify root view MSAA name and role.
  TestViewAccessibilityObject(GetBrowserView(),
                              l10n_util::GetString(IDS_PRODUCT_NAME),
                              ROLE_SYSTEM_CLIENT);
}

// Retrieve accessibility object for toolbar view and verify accessibility info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestToolbarViewAccObj) {
  // Verify toolbar MSAA name and role.
  TestViewAccessibilityObject(GetToolbarView(),
                              l10n_util::GetString(IDS_ACCNAME_TOOLBAR),
                              ROLE_SYSTEM_TOOLBAR);
}

// Retrieve accessibility object for Back button and verify accessibility info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestBackButtonAccObj) {
  // Verify Back button MSAA name and role.
  TestViewAccessibilityObject(
      GetToolbarView()->GetViewByID(VIEW_ID_BACK_BUTTON),
      l10n_util::GetString(IDS_ACCNAME_BACK), ROLE_SYSTEM_BUTTONDROPDOWN);
}

// Retrieve accessibility object for Forward button and verify accessibility
// info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestForwardButtonAccObj) {
  // Verify Forward button MSAA name and role.
  TestViewAccessibilityObject(
      GetToolbarView()->GetViewByID(VIEW_ID_FORWARD_BUTTON),
      l10n_util::GetString(IDS_ACCNAME_FORWARD), ROLE_SYSTEM_BUTTONDROPDOWN);
}

// Retrieve accessibility object for Reload button and verify accessibility
// info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestReloadButtonAccObj) {
  // Verify Reload button MSAA name and role.
  TestViewAccessibilityObject(
      GetToolbarView()->GetViewByID(VIEW_ID_RELOAD_BUTTON),
      l10n_util::GetString(IDS_ACCNAME_RELOAD), ROLE_SYSTEM_PUSHBUTTON);
}

// Retrieve accessibility object for Home button and verify accessibility info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestHomeButtonAccObj) {
  // Verify Home button MSAA name and role.
  TestViewAccessibilityObject(
      GetToolbarView()->GetViewByID(VIEW_ID_HOME_BUTTON),
      l10n_util::GetString(IDS_ACCNAME_HOME), ROLE_SYSTEM_PUSHBUTTON);
}

// Retrieve accessibility object for Star button and verify accessibility info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest,
                       TestStarButtonAccObj) {
  // Verify Star button MSAA name and role.
  TestViewAccessibilityObject(
      GetToolbarView()->GetViewByID(VIEW_ID_STAR_BUTTON),
      l10n_util::GetString(IDS_ACCNAME_STAR), ROLE_SYSTEM_PUSHBUTTON);
}

// Retrieve accessibility object for location bar view and verify accessibility
// info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest,
                       TestLocationBarViewAccObj) {
  // Verify location bar MSAA name and role.
  TestViewAccessibilityObject(GetLocationBarView(),
                              l10n_util::GetString(IDS_ACCNAME_LOCATION),
                              ROLE_SYSTEM_GROUPING);
}

// Retrieve accessibility object for Go button and verify accessibility info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestGoButtonAccObj) {
  // Verify Go button MSAA name and role.
  TestViewAccessibilityObject(GetToolbarView()->GetViewByID(VIEW_ID_GO_BUTTON),
                              l10n_util::GetString(IDS_ACCNAME_GO),
                              ROLE_SYSTEM_PUSHBUTTON);
}

// Retrieve accessibility object for Page menu button and verify accessibility
// info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestPageMenuAccObj) {
  // Verify Page menu button MSAA name and role.
  TestViewAccessibilityObject(GetToolbarView()->GetViewByID(VIEW_ID_PAGE_MENU),
                              l10n_util::GetString(IDS_ACCNAME_PAGE),
                              ROLE_SYSTEM_BUTTONMENU);
}

// Retrieve accessibility object for App menu button and verify accessibility
// info.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest, TestAppMenuAccObj) {
  // Verify App menu button MSAA name and role.
  TestViewAccessibilityObject(GetToolbarView()->GetViewByID(VIEW_ID_APP_MENU),
                              l10n_util::GetString(IDS_ACCNAME_APP),
                              ROLE_SYSTEM_BUTTONMENU);
}

IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest,
                       TestBookmarkBarViewAccObj) {
  TestViewAccessibilityObject(GetBookmarkBarView(),
                              l10n_util::GetString(IDS_ACCNAME_BOOKMARKS),
                              ROLE_SYSTEM_TOOLBAR);
}

// Fails, http://crbug.com/44486.
IN_PROC_BROWSER_TEST_F(BrowserViewsAccessibilityTest,
                       FAILS_TestAboutChromeViewAccObj) {
  //  Firstly, test that the WindowDelegate got updated.
  views::Window* aboutChromeWindow = GetBrowserView()->ShowAboutChromeDialog();
  EXPECT_STREQ(aboutChromeWindow->GetDelegate()->GetWindowTitle().c_str(),
               l10n_util::GetString(IDS_ABOUT_CHROME_TITLE).c_str());
  EXPECT_EQ(aboutChromeWindow->GetDelegate()->accessible_role(),
            AccessibilityTypes::ROLE_DIALOG);

  // Also test the accessibility object directly.
  IAccessible* acc_obj = NULL;
  HRESULT hr =
    ::AccessibleObjectFromWindow(aboutChromeWindow->GetNativeWindow(),
                                 OBJID_CLIENT,
                                 IID_IAccessible,
                                 reinterpret_cast<void**>(&acc_obj));
  ASSERT_EQ(S_OK, hr);
  ASSERT_TRUE(NULL != acc_obj);

  TestAccessibilityInfo(acc_obj, l10n_util::GetString(IDS_ABOUT_CHROME_TITLE),
                        ROLE_SYSTEM_DIALOG);

  acc_obj->Release();
}
}  // Namespace.

<|endoftext|>


<commit_before>/*
 * Swift Parallel Scripting Language (http://swift-lang.org)
 *
 * Copyright 2012-2014 University of Chicago
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * coaster-channel.cpp
 *
 *  Created on: Jun 9, 2012
 *      Author: mike
 */

#include "CoasterChannel.h"
#include "CoasterError.h"
#include "CmdCBCV.h"
#include "HeartBeatCommand.h"
#include "ShutdownCommand.h"
#include <cassert>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "Logger.h"

#include <algorithm>
#include <cassert>
#include <sstream>

using namespace Coaster;

using std::exception;
using std::list;
using std::map;
using std::min;
using std::string;

static int socksend(int fd, const char* buf, int len, int flags);
static void pp(char* dest, const char* src, int len);

class HeartBeatCommand;

DataChunk::DataChunk() {
	bufpos = 0;
}

DataChunk::DataChunk(Buffer* pbuf, ChannelCallback* pcb) {
	buf = pbuf;
	cb = pcb;
	bufpos = 0;
}

void DataChunk::reset() {
	bufpos = 0;
}

void HeartbeatCallback::errorReceived(Command* cmd, string* message, RemoteCoasterException* details) {
	delete cmd;
	LogWarn << "Heartbeat failed: " << message << endl;
}

void HeartbeatCallback::replyReceived(Command* cmd) {
	delete cmd;
}

CoasterChannel::CoasterChannel(CoasterClient* client, CoasterLoop* loop,
			       HandlerFactory* handlerFactory) :
			       rhdr_buf(HEADER_LENGTH),
			       rhdr(&rhdr_buf, (ChannelCallback*)NULL) {
	assert(loop != NULL);
	assert(client != NULL);
	sockFD = 0;
	this->handlerFactory = handlerFactory;
	tagSeq = rand() % 65536;
	this->loop = loop;
	this->client = client;

	readState = READ_STATE_HDR;
	msg.buf = NULL;
}

CoasterChannel::~CoasterChannel() {
	for (map<int, Handler*>::iterator it = handlers.begin();
	     it != handlers.end(); ++it) {
		delete it->second;
	}

}

void CoasterChannel::start() {
	if (sockFD == 0) {
		throw CoasterError("channel start() called but no socket set");
	}
}

void CoasterChannel::shutdown() {
	ShutdownCommand* cmd = new ShutdownCommand();
	CmdCBCV callback;

	cmd->send(this, &callback);

	callback.wait();
}

int CoasterChannel::getSockFD() {
	return sockFD;
}

void CoasterChannel::setSockFD(int psockFD) {
	sockFD = psockFD;
}

void CoasterChannel::read() {
	switch(readState) {
		case READ_STATE_HDR:
			if (read(&rhdr)) {
				// full header read
				decodeHeader(&rtag, &rflags, &rlen);
				// setup message buffer for rest of message
				msg.reset();
				// Buffer should be cleared
				assert(msg.buf == NULL);
				msg.buf = new DynamicBuffer(rlen);
				readState = READ_STATE_DATA;
			}
			else {
				break;
			}
			/* no break */
		case READ_STATE_DATA:
			if (read(&msg)) {
				// full message read
				dispatchData();
				rhdr.reset();
				readState = READ_STATE_HDR;
			}
			break;
	}
}

bool CoasterChannel::read(DataChunk* dc) {
	int ret = recv(sockFD, dc->buf->getModifiableData(), (dc->buf->getLen() - dc->bufpos), MSG_DONTWAIT);
	if (ret == -1) {
		if (errno == EAGAIN || errno == EWOULDBLOCK) {
			return false;
		}
		else {
			throw CoasterError("Socket read error: %s", strerror(errno));
		}
	}
	else {
		dc->bufpos += ret;
		if (dc->bufpos == dc->buf->getLen()) {
			return true;
		}
		else {
			return false;
		}
	}
}

void CoasterChannel::dispatchData() {
	if (rflags & FLAG_REPLY) {
		dispatchReply();
	}
	else {
		dispatchRequest();
	}
}

void CoasterChannel::dispatchReply() {
	if (commands.count(rtag) == 0) {
		throw new CoasterError("Received reply to unknown command (tag: %d)", rtag);
	}
	LogDebug << "dispatching reply " << rtag << ", " << rflags << endl;
	Command* cmd = commands[rtag];
	if (rflags & FLAG_SIGNAL) {
		try {
			cmd->signalReceived(msg.detachBuffer());
		}
		catch (exception &e) {
			LogWarn << "Command::signalReceived() threw exception: " << e.what() << endl;
		}
		catch (...) {
			LogWarn << "Command::signalReceived() threw unknown exception" << endl;
		}
	}
	else {
		cmd->dataReceived(msg.detachBuffer(), rflags);
		if (rflags & FLAG_FINAL) {
			unregisterCommand(cmd);
			try {
				cmd->receiveCompleted(rflags);
			}
			catch (exception &e) {
				LogWarn << "Command::receiveCompleted() threw exception: " << e.what() << endl;
			}
			catch (...) {
				LogWarn << "Command::receiveCompleted() threw unknown exception" << endl;
			}
		}
	}
}

void CoasterChannel::dispatchRequest() {
	if (handlers.count(rtag) == 0) {
		// initial request
		string name;
		msg.buf->str(name);

		// Done with data
		msg.deleteBuffer();

		LogDebug << "Handling initial request for " << name << endl;
		Handler* h = handlerFactory->newInstance(name);
		if (h == NULL) {
			LogWarn << "Unknown handler: " << name << endl;
		}
		else {
			registerHandler(rtag, h);
		}
	}
	else {
		Handler* h = handlers[rtag];
		if (rflags & FLAG_SIGNAL) {
			try {
				h->signalReceived(msg.detachBuffer());
			}
			catch (exception &e) {
				LogWarn << "Handler::signalReceived() threw exception: " << e.what() << endl;
			}
			catch (...) {
				LogWarn << "Handler::signalReceived() threw unknown exception" << endl;
			}
			unregisterHandler(h);
		}
		else {
			h->dataReceived(msg.detachBuffer(), rflags);

			if (rflags & FLAG_FINAL) {
				try {
					h->receiveCompleted(rflags);
				}
				catch (exception &e) {
					LogWarn << "Handler::receiveCompleted() threw exception: " << e.what() << endl;
				}
				catch (...) {
					LogWarn << "Handler::receiveCompleted() threw unknown exception" << endl;
				}
				unregisterHandler(h);
			}
		}
	}
}

/**
 * Attempts to write the data chunk at the front of the queue. If the entire chunk is
 * written, it removes it from the queue and returns true. Returns false there are bytes
 * left to write in the chunk
 */
bool CoasterChannel::write() { Lock::Scoped l(writeLock);
	DataChunk* dc = sendQueue.front();

	Buffer* buf = dc->buf;

	int ret = socksend(sockFD, buf->getData(), (buf->getLen() - dc->bufpos), MSG_DONTWAIT);
	char tmp[81];
	pp(tmp, buf->getData(), min(80, ret));
	LogDebug << this << " sent " << ret << " bytes: " << tmp << endl;
	if (ret == -1) {
		if (errno == EAGAIN || errno == EWOULDBLOCK) {
			return false;
		}
		else {
			throw CoasterError("Socket write error: %s", strerror(errno));
		}
	}
	else {
		dc->bufpos += ret;
		if (dc->bufpos == buf->getLen()) {
			sendQueue.pop_front();
			if (dc->cb != NULL) {
				try {
					dc->cb->dataSent(buf);
				}
				catch (exception &e) {
					LogWarn << "Callback threw exception: " << e.what() << endl;
				}
				catch (...) {
					LogWarn << "Callback threw unknown exception" << endl;
				}
			}
			delete dc;
			return true;
		}
		else {
			return false;
		}
	}
}

DataChunk* CoasterChannel::makeHeader(int tag, Buffer* buf, int flags) {
	DynamicBuffer *hdrbuf = new DynamicBuffer(HEADER_LENGTH);
	DataChunk* whdr = new DataChunk(hdrbuf, &DeleteBufferCallback::CALLBACK);
	Buffer* hdr = whdr->buf;
	hdr->putInt(0, tag);
	hdr->putInt(4, flags);
	hdr->putInt(8, buf->getLen());
	int hcsum = tag ^ flags ^ buf->getLen();
	hdr->putInt(12, hcsum);
	hdr->putInt(16, 0);
	char tmp[128];
	sprintf(tmp, "Send[tag: 0x%08x, flags: 0x%08x, len: 0x%08d, hcsum: 0x%08x]", tag, flags, buf->getLen(), hcsum);
	LogDebug << tmp << endl;
	return whdr;
}

void CoasterChannel::decodeHeader(int* tag, int* flags, int* len) {
	Buffer* buf = rhdr.buf;
	*tag = buf->getInt(0);
	*flags = buf->getInt(4);
	*len = buf->getInt(8);
	int hcsum = buf->getInt(12);
	int ccsum = *tag ^ *flags ^ *len;
	char tmp[128];
	sprintf(tmp, "Recv[tag: 0x%08x, flags: 0x%08x, len: 0x%08d, hcsum: 0x%08x]", *tag, *flags, *len, hcsum);
	LogDebug << tmp << endl;
	if (hcsum != ccsum) {
		throw CoasterError("Header checksum failed. Received checksum: %d, computed checksum: %d", hcsum, ccsum);
	}
}

void CoasterChannel::registerCommand(Command* cmd) {
	int tag = tagSeq++;
	cmd->setTag(tag);
	commands[tag] = cmd;
}

void CoasterChannel::unregisterCommand(Command* cmd) {
	commands.erase(cmd->getTag());
}

void CoasterChannel::registerHandler(int tag, Handler* h) {
	h->setTag(tag);
	handlers[tag] = h;
	h->setChannel(this);
}

void CoasterChannel::unregisterHandler(Handler* h) {
	handlers.erase(h->getTag());
	delete h;
}

void CoasterChannel::send(int tag, Buffer* buf, int flags, ChannelCallback* cb) { Lock::Scoped l(writeLock);
	assert(buf != NULL);
	assert(buf->getData() != NULL);
	sendQueue.push_back(makeHeader(tag, buf, flags));
	sendQueue.push_back(new DataChunk(buf, cb));
	loop->requestWrite(this, 2);
}

CoasterClient* CoasterChannel::getClient() {
	return client;
}

void CoasterChannel::checkHeartbeat() {
	// TODO: this can be sent after shutdown
	Command* cmd = new HeartBeatCommand();
	cmd->send(this, &heartbeatCB);
}

const string& CoasterChannel::getURL() const {
	return client->getURL();
}

/*
 * Without this, GCC gets confused by CoasterChannel::send.
 */
static int socksend(int fd, const char* buf, int len, int flags) {
	return send(fd, buf, len, flags);
}

static void pp(char* dest, const char* src, int len) {
	for(int i = 0; i < len; i++) {
		char c = src[i];
		if (c < '0' || c > 127) {
			c = '.';
		}
		dest[i] = c;
	}
	dest[len] = 0;
}
<commit_msg>Avoid leaking command<commit_after>/*
 * Swift Parallel Scripting Language (http://swift-lang.org)
 *
 * Copyright 2012-2014 University of Chicago
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * coaster-channel.cpp
 *
 *  Created on: Jun 9, 2012
 *      Author: mike
 */

#include "CoasterChannel.h"
#include "CoasterError.h"
#include "HeartBeatCommand.h"
#include "ShutdownCommand.h"
#include <cassert>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "Logger.h"

#include <algorithm>
#include <cassert>
#include <sstream>

using namespace Coaster;

using std::exception;
using std::list;
using std::map;
using std::min;
using std::string;

static int socksend(int fd, const char* buf, int len, int flags);
static void pp(char* dest, const char* src, int len);

class HeartBeatCommand;

DataChunk::DataChunk() {
	bufpos = 0;
}

DataChunk::DataChunk(Buffer* pbuf, ChannelCallback* pcb) {
	buf = pbuf;
	cb = pcb;
	bufpos = 0;
}

void DataChunk::reset() {
	bufpos = 0;
}

void HeartbeatCallback::errorReceived(Command* cmd, string* message, RemoteCoasterException* details) {
	delete cmd;
	LogWarn << "Heartbeat failed: " << message << endl;
}

void HeartbeatCallback::replyReceived(Command* cmd) {
	delete cmd;
}

CoasterChannel::CoasterChannel(CoasterClient* client, CoasterLoop* loop,
			       HandlerFactory* handlerFactory) :
			       rhdr_buf(HEADER_LENGTH),
			       rhdr(&rhdr_buf, (ChannelCallback*)NULL) {
	assert(loop != NULL);
	assert(client != NULL);
	sockFD = 0;
	this->handlerFactory = handlerFactory;
	tagSeq = rand() % 65536;
	this->loop = loop;
	this->client = client;

	readState = READ_STATE_HDR;
	msg.buf = NULL;
}

CoasterChannel::~CoasterChannel() {
	for (map<int, Handler*>::iterator it = handlers.begin();
	     it != handlers.end(); ++it) {
		delete it->second;
	}

}

void CoasterChannel::start() {
	if (sockFD == 0) {
		throw CoasterError("channel start() called but no socket set");
	}
}

void CoasterChannel::shutdown() {
	ShutdownCommand cmd;
        cmd.execute(this);
}

int CoasterChannel::getSockFD() {
	return sockFD;
}

void CoasterChannel::setSockFD(int psockFD) {
	sockFD = psockFD;
}

void CoasterChannel::read() {
	switch(readState) {
		case READ_STATE_HDR:
			if (read(&rhdr)) {
				// full header read
				decodeHeader(&rtag, &rflags, &rlen);
				// setup message buffer for rest of message
				msg.reset();
				// Buffer should be cleared
				assert(msg.buf == NULL);
				msg.buf = new DynamicBuffer(rlen);
				readState = READ_STATE_DATA;
			}
			else {
				break;
			}
			/* no break */
		case READ_STATE_DATA:
			if (read(&msg)) {
				// full message read
				dispatchData();
				rhdr.reset();
				readState = READ_STATE_HDR;
			}
			break;
	}
}

bool CoasterChannel::read(DataChunk* dc) {
	int ret = recv(sockFD, dc->buf->getModifiableData(), (dc->buf->getLen() - dc->bufpos), MSG_DONTWAIT);
	if (ret == -1) {
		if (errno == EAGAIN || errno == EWOULDBLOCK) {
			return false;
		}
		else {
			throw CoasterError("Socket read error: %s", strerror(errno));
		}
	}
	else {
		dc->bufpos += ret;
		if (dc->bufpos == dc->buf->getLen()) {
			return true;
		}
		else {
			return false;
		}
	}
}

void CoasterChannel::dispatchData() {
	if (rflags & FLAG_REPLY) {
		dispatchReply();
	}
	else {
		dispatchRequest();
	}
}

void CoasterChannel::dispatchReply() {
	if (commands.count(rtag) == 0) {
		throw new CoasterError("Received reply to unknown command (tag: %d)", rtag);
	}
	LogDebug << "dispatching reply " << rtag << ", " << rflags << endl;
	Command* cmd = commands[rtag];
	if (rflags & FLAG_SIGNAL) {
		try {
			cmd->signalReceived(msg.detachBuffer());
		}
		catch (exception &e) {
			LogWarn << "Command::signalReceived() threw exception: " << e.what() << endl;
		}
		catch (...) {
			LogWarn << "Command::signalReceived() threw unknown exception" << endl;
		}
	}
	else {
		cmd->dataReceived(msg.detachBuffer(), rflags);
		if (rflags & FLAG_FINAL) {
			unregisterCommand(cmd);
			try {
				cmd->receiveCompleted(rflags);
			}
			catch (exception &e) {
				LogWarn << "Command::receiveCompleted() threw exception: " << e.what() << endl;
			}
			catch (...) {
				LogWarn << "Command::receiveCompleted() threw unknown exception" << endl;
			}
		}
	}
}

void CoasterChannel::dispatchRequest() {
	if (handlers.count(rtag) == 0) {
		// initial request
		string name;
		msg.buf->str(name);

		// Done with data
		msg.deleteBuffer();

		LogDebug << "Handling initial request for " << name << endl;
		Handler* h = handlerFactory->newInstance(name);
		if (h == NULL) {
			LogWarn << "Unknown handler: " << name << endl;
		}
		else {
			registerHandler(rtag, h);
		}
	}
	else {
		Handler* h = handlers[rtag];
		if (rflags & FLAG_SIGNAL) {
			try {
				h->signalReceived(msg.detachBuffer());
			}
			catch (exception &e) {
				LogWarn << "Handler::signalReceived() threw exception: " << e.what() << endl;
			}
			catch (...) {
				LogWarn << "Handler::signalReceived() threw unknown exception" << endl;
			}
			unregisterHandler(h);
		}
		else {
			h->dataReceived(msg.detachBuffer(), rflags);

			if (rflags & FLAG_FINAL) {
				try {
					h->receiveCompleted(rflags);
				}
				catch (exception &e) {
					LogWarn << "Handler::receiveCompleted() threw exception: " << e.what() << endl;
				}
				catch (...) {
					LogWarn << "Handler::receiveCompleted() threw unknown exception" << endl;
				}
				unregisterHandler(h);
			}
		}
	}
}

/**
 * Attempts to write the data chunk at the front of the queue. If the entire chunk is
 * written, it removes it from the queue and returns true. Returns false there are bytes
 * left to write in the chunk
 */
bool CoasterChannel::write() { Lock::Scoped l(writeLock);
	DataChunk* dc = sendQueue.front();

	Buffer* buf = dc->buf;

	int ret = socksend(sockFD, buf->getData(), (buf->getLen() - dc->bufpos), MSG_DONTWAIT);
	char tmp[81];
	pp(tmp, buf->getData(), min(80, ret));
	LogDebug << this << " sent " << ret << " bytes: " << tmp << endl;
	if (ret == -1) {
		if (errno == EAGAIN || errno == EWOULDBLOCK) {
			return false;
		}
		else {
			throw CoasterError("Socket write error: %s", strerror(errno));
		}
	}
	else {
		dc->bufpos += ret;
		if (dc->bufpos == buf->getLen()) {
			sendQueue.pop_front();
			if (dc->cb != NULL) {
				try {
					dc->cb->dataSent(buf);
				}
				catch (exception &e) {
					LogWarn << "Callback threw exception: " << e.what() << endl;
				}
				catch (...) {
					LogWarn << "Callback threw unknown exception" << endl;
				}
			}
			delete dc;
			return true;
		}
		else {
			return false;
		}
	}
}

DataChunk* CoasterChannel::makeHeader(int tag, Buffer* buf, int flags) {
	DynamicBuffer *hdrbuf = new DynamicBuffer(HEADER_LENGTH);
	DataChunk* whdr = new DataChunk(hdrbuf, &DeleteBufferCallback::CALLBACK);
	Buffer* hdr = whdr->buf;
	hdr->putInt(0, tag);
	hdr->putInt(4, flags);
	hdr->putInt(8, buf->getLen());
	int hcsum = tag ^ flags ^ buf->getLen();
	hdr->putInt(12, hcsum);
	hdr->putInt(16, 0);
	char tmp[128];
	sprintf(tmp, "Send[tag: 0x%08x, flags: 0x%08x, len: 0x%08d, hcsum: 0x%08x]", tag, flags, buf->getLen(), hcsum);
	LogDebug << tmp << endl;
	return whdr;
}

void CoasterChannel::decodeHeader(int* tag, int* flags, int* len) {
	Buffer* buf = rhdr.buf;
	*tag = buf->getInt(0);
	*flags = buf->getInt(4);
	*len = buf->getInt(8);
	int hcsum = buf->getInt(12);
	int ccsum = *tag ^ *flags ^ *len;
	char tmp[128];
	sprintf(tmp, "Recv[tag: 0x%08x, flags: 0x%08x, len: 0x%08d, hcsum: 0x%08x]", *tag, *flags, *len, hcsum);
	LogDebug << tmp << endl;
	if (hcsum != ccsum) {
		throw CoasterError("Header checksum failed. Received checksum: %d, computed checksum: %d", hcsum, ccsum);
	}
}

void CoasterChannel::registerCommand(Command* cmd) {
	int tag = tagSeq++;
	cmd->setTag(tag);
	commands[tag] = cmd;
}

void CoasterChannel::unregisterCommand(Command* cmd) {
	commands.erase(cmd->getTag());
}

void CoasterChannel::registerHandler(int tag, Handler* h) {
	h->setTag(tag);
	handlers[tag] = h;
	h->setChannel(this);
}

void CoasterChannel::unregisterHandler(Handler* h) {
	handlers.erase(h->getTag());
	delete h;
}

void CoasterChannel::send(int tag, Buffer* buf, int flags, ChannelCallback* cb) { Lock::Scoped l(writeLock);
	assert(buf != NULL);
	assert(buf->getData() != NULL);
	sendQueue.push_back(makeHeader(tag, buf, flags));
	sendQueue.push_back(new DataChunk(buf, cb));
	loop->requestWrite(this, 2);
}

CoasterClient* CoasterChannel::getClient() {
	return client;
}

void CoasterChannel::checkHeartbeat() {
	// TODO: this can be sent after shutdown
	Command* cmd = new HeartBeatCommand();
	cmd->send(this, &heartbeatCB);
}

const string& CoasterChannel::getURL() const {
	return client->getURL();
}

/*
 * Without this, GCC gets confused by CoasterChannel::send.
 */
static int socksend(int fd, const char* buf, int len, int flags) {
	return send(fd, buf, len, flags);
}

static void pp(char* dest, const char* src, int len) {
	for(int i = 0; i < len; i++) {
		char c = src[i];
		if (c < '0' || c > 127) {
			c = '.';
		}
		dest[i] = c;
	}
	dest[len] = 0;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: MTypeConverter.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: obo $ $Date: 2006-07-10 14:30:20 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _CONNECTIVITY_MAB_TYPECONVERTER_HXX_
#define _CONNECTIVITY_MAB_TYPECONVERTER_HXX_

#include <rtl/ustring.hxx>

#include<string>

namespace connectivity
{
    namespace mozab
    {
        class MTypeConverter
        {
        public:
            static void  ouStringToNsString(const ::rtl::OUString&, nsString&);
            static void  nsStringToOUString(const nsString&, ::rtl::OUString&);
            static void  prUnicharToOUString(const PRUnichar*, ::rtl::OUString&);
            // Use free() for the following 3 calls.
            static char *ouStringToCCharStringAscii(const ::rtl::OUString&);
            static char *nsStringToCCharStringAscii(const nsString&);
            static char *ouStringToCCharStringUtf8(const ::rtl::OUString&);
            // Convert to stl-string.
            static ::std::string ouStringToStlString(const ::rtl::OUString&);
            static ::std::string nsStringToStlString(const nsString&);

        private:
            MTypeConverter() {};
        };
    }
}

#endif // _CONNECTIVITY_MAB_TYPECONVERTER_HXX_

<commit_msg>INTEGRATION: CWS changefileheader (1.3.262); FILE MERGED 2008/03/28 15:23:59 rt 1.3.262.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: MTypeConverter.hxx,v $
 * $Revision: 1.4 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifndef _CONNECTIVITY_MAB_TYPECONVERTER_HXX_
#define _CONNECTIVITY_MAB_TYPECONVERTER_HXX_

#include <rtl/ustring.hxx>

#include<string>

namespace connectivity
{
    namespace mozab
    {
        class MTypeConverter
        {
        public:
            static void  ouStringToNsString(const ::rtl::OUString&, nsString&);
            static void  nsStringToOUString(const nsString&, ::rtl::OUString&);
            static void  prUnicharToOUString(const PRUnichar*, ::rtl::OUString&);
            // Use free() for the following 3 calls.
            static char *ouStringToCCharStringAscii(const ::rtl::OUString&);
            static char *nsStringToCCharStringAscii(const nsString&);
            static char *ouStringToCCharStringUtf8(const ::rtl::OUString&);
            // Convert to stl-string.
            static ::std::string ouStringToStlString(const ::rtl::OUString&);
            static ::std::string nsStringToStlString(const nsString&);

        private:
            MTypeConverter() {};
        };
    }
}

#endif // _CONNECTIVITY_MAB_TYPECONVERTER_HXX_

<|endoftext|>


<commit_before>// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/renderer/pepper/pepper_media_stream_audio_track_host.h"

#include <algorithm>

#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/numerics/safe_math.h"
#include "media/base/audio_bus.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/ppb_audio_buffer.h"
#include "ppapi/host/dispatch_host_message.h"
#include "ppapi/host/host_message_context.h"
#include "ppapi/host/ppapi_host.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/shared_impl/media_stream_audio_track_shared.h"
#include "ppapi/shared_impl/media_stream_buffer.h"

using media::AudioParameters;
using ppapi::host::HostMessageContext;
using ppapi::MediaStreamAudioTrackShared;

namespace {

// Audio buffer durations in milliseconds.
const uint32_t kMinDuration = 10;
const uint32_t kDefaultDuration = 10;

const int32_t kDefaultNumberOfBuffers = 4;
const int32_t kMaxNumberOfBuffers = 1000;  // 10 sec

// Returns true if the |sample_rate| is supported in
// |PP_AudioBuffer_SampleRate|, otherwise false.
PP_AudioBuffer_SampleRate GetPPSampleRate(int sample_rate) {
  switch (sample_rate) {
    case 8000:
      return PP_AUDIOBUFFER_SAMPLERATE_8000;
    case 16000:
      return PP_AUDIOBUFFER_SAMPLERATE_16000;
    case 22050:
      return PP_AUDIOBUFFER_SAMPLERATE_22050;
    case 32000:
      return PP_AUDIOBUFFER_SAMPLERATE_32000;
    case 44100:
      return PP_AUDIOBUFFER_SAMPLERATE_44100;
    case 48000:
      return PP_AUDIOBUFFER_SAMPLERATE_48000;
    case 96000:
      return PP_AUDIOBUFFER_SAMPLERATE_96000;
    case 192000:
      return PP_AUDIOBUFFER_SAMPLERATE_192000;
    default:
      return PP_AUDIOBUFFER_SAMPLERATE_UNKNOWN;
  }
}

}  // namespace

namespace content {

PepperMediaStreamAudioTrackHost::AudioSink::AudioSink(
    PepperMediaStreamAudioTrackHost* host)
    : host_(host),
      active_buffer_index_(-1),
      active_buffers_generation_(0),
      active_buffer_frame_offset_(0),
      buffers_generation_(0),
      main_message_loop_proxy_(base::MessageLoopProxy::current()),
      number_of_buffers_(kDefaultNumberOfBuffers),
      bytes_per_second_(0),
      bytes_per_frame_(0),
      user_buffer_duration_(kDefaultDuration),
      weak_factory_(this) {}

PepperMediaStreamAudioTrackHost::AudioSink::~AudioSink() {
  DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
}

void PepperMediaStreamAudioTrackHost::AudioSink::EnqueueBuffer(int32_t index) {
  DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
  DCHECK_GE(index, 0);
  DCHECK_LT(index, host_->buffer_manager()->number_of_buffers());
  base::AutoLock lock(lock_);
  buffers_.push_back(index);
}

int32_t PepperMediaStreamAudioTrackHost::AudioSink::Configure(
    int32_t number_of_buffers, int32_t duration,
    const ppapi::host::ReplyMessageContext& context) {
  DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());

  if (pending_configure_reply_.is_valid()) {
    return PP_ERROR_INPROGRESS;
  }
  pending_configure_reply_ = context;

  bool changed = false;
  if (number_of_buffers != number_of_buffers_)
    changed = true;
  if (duration != 0 && duration != user_buffer_duration_) {
    user_buffer_duration_ = duration;
    changed = true;
  }
  number_of_buffers_ = number_of_buffers;

  if (changed) {
    // Initialize later in OnSetFormat if bytes_per_second_ is not known yet.
    if (bytes_per_second_ > 0 && bytes_per_frame_ > 0)
      InitBuffers();
  } else {
    SendConfigureReply(PP_OK);
  }
  return PP_OK_COMPLETIONPENDING;
}

void PepperMediaStreamAudioTrackHost::AudioSink::SendConfigureReply(
    int32_t result) {
  if (pending_configure_reply_.is_valid()) {
    pending_configure_reply_.params.set_result(result);
    host_->host()->SendReply(
        pending_configure_reply_,
        PpapiPluginMsg_MediaStreamAudioTrack_ConfigureReply());
    pending_configure_reply_ = ppapi::host::ReplyMessageContext();
  }
}

void PepperMediaStreamAudioTrackHost::AudioSink::SetFormatOnMainThread(
    int bytes_per_second, int bytes_per_frame) {
  bytes_per_second_ = bytes_per_second;
  bytes_per_frame_ = bytes_per_frame;
  InitBuffers();
}

void PepperMediaStreamAudioTrackHost::AudioSink::InitBuffers() {
  DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
  {
    base::AutoLock lock(lock_);
    // Clear |buffers_|, so the audio thread will drop all incoming audio data.
    buffers_.clear();
    buffers_generation_++;
  }
  int32_t frame_rate = bytes_per_second_ / bytes_per_frame_;
  base::CheckedNumeric<int32_t> frames_per_buffer = user_buffer_duration_;
  frames_per_buffer *= frame_rate;
  frames_per_buffer /= base::Time::kMillisecondsPerSecond;
  base::CheckedNumeric<int32_t> buffer_audio_size =
      frames_per_buffer * bytes_per_frame_;
  // The size is slightly bigger than necessary, because 8 extra bytes are
  // added into the struct. Also see |MediaStreamBuffer|. Also, the size of the
  // buffer may be larger than requested, since the size of each buffer will be
  // 4-byte aligned.
  base::CheckedNumeric<int32_t> buffer_size = buffer_audio_size;
  buffer_size += sizeof(ppapi::MediaStreamBuffer::Audio);
  DCHECK_GT(buffer_size.ValueOrDie(), 0);

  // We don't need to hold |lock_| during |host->InitBuffers()| call, because
  // we just cleared |buffers_| , so the audio thread will drop all incoming
  // audio data, and not use buffers in |host_|.
  bool result = host_->InitBuffers(number_of_buffers_,
                                   buffer_size.ValueOrDie(),
                                   kRead);
  if (!result) {
    SendConfigureReply(PP_ERROR_NOMEMORY);
    return;
  }

  // Fill the |buffers_|, so the audio thread can continue receiving audio data.
  base::AutoLock lock(lock_);
  output_buffer_size_ = buffer_audio_size.ValueOrDie();
  for (int32_t i = 0; i < number_of_buffers_; ++i) {
    int32_t index = host_->buffer_manager()->DequeueBuffer();
    DCHECK_GE(index, 0);
    buffers_.push_back(index);
  }

  SendConfigureReply(PP_OK);
}

void PepperMediaStreamAudioTrackHost::AudioSink::
    SendEnqueueBufferMessageOnMainThread(int32_t index,
                                         int32_t buffers_generation) {
  DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
  // If |InitBuffers()| is called after this task being posted from the audio
  // thread, the buffer should become invalid already. We should ignore it.
  // And because only the main thread modifies the |buffers_generation_|,
  // so we don't need to lock |lock_| here (main thread).
  if (buffers_generation == buffers_generation_)
    host_->SendEnqueueBufferMessageToPlugin(index);
}

void PepperMediaStreamAudioTrackHost::AudioSink::OnData(
    const media::AudioBus& audio_bus,
    base::TimeTicks estimated_capture_time) {
  DCHECK(audio_thread_checker_.CalledOnValidThread());
  DCHECK(audio_params_.IsValid());
  DCHECK_EQ(audio_bus.channels(), audio_params_.channels());
  // Here, |audio_params_.frames_per_buffer()| refers to the incomming audio
  // buffer. However, this doesn't necessarily equal
  // |buffer->number_of_samples|, which is configured by the user when they set
  // buffer duration.
  DCHECK_EQ(audio_bus.frames(), audio_params_.frames_per_buffer());
  DCHECK(!estimated_capture_time.is_null());

  if (first_frame_capture_time_.is_null())
    first_frame_capture_time_ = estimated_capture_time;

  const int bytes_per_frame = audio_params_.GetBytesPerFrame();

  base::AutoLock lock(lock_);
  for (int frame_offset = 0; frame_offset < audio_bus.frames(); ) {
    if (active_buffers_generation_ != buffers_generation_) {
      // Buffers have changed, so drop the active buffer.
      active_buffer_index_ = -1;
    }
    if (active_buffer_index_ == -1 && !buffers_.empty()) {
      active_buffers_generation_ = buffers_generation_;
      active_buffer_frame_offset_ = 0;
      active_buffer_index_ = buffers_.front();
      buffers_.pop_front();
    }
    if (active_buffer_index_ == -1) {
      // Eek! We're dropping frames. Bad, bad, bad!
      break;
    }

    // TODO(penghuang): Support re-sampling and channel mixing by using
    // media::AudioConverter.
    ppapi::MediaStreamBuffer::Audio* buffer =
        &(host_->buffer_manager()->GetBufferPointer(active_buffer_index_)
          ->audio);
    if (active_buffer_frame_offset_ == 0) {
      // The active buffer is new, so initialise the header and metadata fields.
      buffer->header.size = host_->buffer_manager()->buffer_size();
      buffer->header.type = ppapi::MediaStreamBuffer::TYPE_AUDIO;
      const base::TimeTicks time_at_offset = estimated_capture_time +
          frame_offset * base::TimeDelta::FromSeconds(1) /
              audio_params_.sample_rate();
      buffer->timestamp =
          (time_at_offset - first_frame_capture_time_).InMillisecondsF();
      buffer->sample_rate =
          static_cast<PP_AudioBuffer_SampleRate>(audio_params_.sample_rate());
      buffer->data_size = output_buffer_size_;
      buffer->number_of_channels = audio_params_.channels();
      buffer->number_of_samples = buffer->data_size * audio_params_.channels() /
          bytes_per_frame;
    }

    const int frames_per_buffer =
        buffer->number_of_samples / audio_params_.channels();
    const int frames_to_copy = std::min(
        frames_per_buffer - active_buffer_frame_offset_,
        audio_bus.frames() - frame_offset);
    audio_bus.ToInterleavedPartial(
        frame_offset,
        frames_to_copy,
        audio_params_.bits_per_sample() / 8,
        buffer->data + active_buffer_frame_offset_ * bytes_per_frame);
    active_buffer_frame_offset_ += frames_to_copy;
    frame_offset += frames_to_copy;

    DCHECK_LE(active_buffer_frame_offset_, frames_per_buffer);
    if (active_buffer_frame_offset_ == frames_per_buffer) {
      main_message_loop_proxy_->PostTask(
          FROM_HERE,
          base::Bind(&AudioSink::SendEnqueueBufferMessageOnMainThread,
                     weak_factory_.GetWeakPtr(),
                     active_buffer_index_,
                     buffers_generation_));
      active_buffer_index_ = -1;
    }
  }
}

void PepperMediaStreamAudioTrackHost::AudioSink::OnSetFormat(
    const AudioParameters& params) {
  DCHECK(params.IsValid());
  // TODO(amistry): How do you handle the case where the user configures a
  // duration that's shorter than the received buffer duration? One option is to
  // double buffer, where the size of the intermediate ring buffer is at least
  // max(user requested duration, received buffer duration). There are other
  // ways of dealing with it, but which one is "correct"?
  DCHECK_LE(params.GetBufferDuration().InMilliseconds(), kMinDuration);
  DCHECK_EQ(params.bits_per_sample(), 16);
  DCHECK_NE(GetPPSampleRate(params.sample_rate()),
            PP_AUDIOBUFFER_SAMPLERATE_UNKNOWN);

  // TODO(penghuang): support setting format more than once.
  if (audio_params_.IsValid()) {
    DCHECK_EQ(params.sample_rate(), audio_params_.sample_rate());
    DCHECK_EQ(params.bits_per_sample(), audio_params_.bits_per_sample());
    DCHECK_EQ(params.channels(), audio_params_.channels());
  } else {
    audio_thread_checker_.DetachFromThread();
    audio_params_ = params;

    main_message_loop_proxy_->PostTask(
        FROM_HERE,
        base::Bind(&AudioSink::SetFormatOnMainThread,
                   weak_factory_.GetWeakPtr(),
                   params.GetBytesPerSecond(),
                   params.GetBytesPerFrame()));
  }
}

PepperMediaStreamAudioTrackHost::PepperMediaStreamAudioTrackHost(
    RendererPpapiHost* host,
    PP_Instance instance,
    PP_Resource resource,
    const blink::WebMediaStreamTrack& track)
    : PepperMediaStreamTrackHostBase(host, instance, resource),
      track_(track),
      connected_(false),
      audio_sink_(this) {
  DCHECK(!track_.isNull());
}

PepperMediaStreamAudioTrackHost::~PepperMediaStreamAudioTrackHost() {
  OnClose();
}

int32_t PepperMediaStreamAudioTrackHost::OnResourceMessageReceived(
    const IPC::Message& msg,
    HostMessageContext* context) {
  PPAPI_BEGIN_MESSAGE_MAP(PepperMediaStreamAudioTrackHost, msg)
    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
        PpapiHostMsg_MediaStreamAudioTrack_Configure, OnHostMsgConfigure)
  PPAPI_END_MESSAGE_MAP()
  return PepperMediaStreamTrackHostBase::OnResourceMessageReceived(msg,
                                                                   context);
}

int32_t PepperMediaStreamAudioTrackHost::OnHostMsgConfigure(
    HostMessageContext* context,
    const MediaStreamAudioTrackShared::Attributes& attributes) {
  if (!MediaStreamAudioTrackShared::VerifyAttributes(attributes))
    return PP_ERROR_BADARGUMENT;

  int32_t buffers = attributes.buffers
                        ? std::min(kMaxNumberOfBuffers, attributes.buffers)
                        : kDefaultNumberOfBuffers;
  return audio_sink_.Configure(buffers, attributes.duration,
                               context->MakeReplyMessageContext());
}

void PepperMediaStreamAudioTrackHost::OnClose() {
  if (connected_) {
    MediaStreamAudioSink::RemoveFromAudioTrack(&audio_sink_, track_);
    connected_ = false;
  }
  audio_sink_.SendConfigureReply(PP_ERROR_ABORTED);
}

void PepperMediaStreamAudioTrackHost::OnNewBufferEnqueued() {
  int32_t index = buffer_manager()->DequeueBuffer();
  DCHECK_GE(index, 0);
  audio_sink_.EnqueueBuffer(index);
}

void PepperMediaStreamAudioTrackHost::DidConnectPendingHostToResource() {
  if (!connected_) {
    media::AudioParameters format =
        MediaStreamAudioSink::GetFormatFromAudioTrack(track_);
    // Although this should only be called on the audio capture thread, that
    // can't happen until the sink is added to the audio track below.
    if (format.IsValid())
      audio_sink_.OnSetFormat(format);

    MediaStreamAudioSink::AddToAudioTrack(&audio_sink_, track_);
    connected_ = true;
  }
}

}  // namespace content
<commit_msg>Set correct timestamp in PepperMediaStreamAudioTrack.<commit_after>// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/renderer/pepper/pepper_media_stream_audio_track_host.h"

#include <algorithm>

#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/numerics/safe_math.h"
#include "media/base/audio_bus.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/ppb_audio_buffer.h"
#include "ppapi/host/dispatch_host_message.h"
#include "ppapi/host/host_message_context.h"
#include "ppapi/host/ppapi_host.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/shared_impl/media_stream_audio_track_shared.h"
#include "ppapi/shared_impl/media_stream_buffer.h"

using media::AudioParameters;
using ppapi::host::HostMessageContext;
using ppapi::MediaStreamAudioTrackShared;

namespace {

// Audio buffer durations in milliseconds.
const uint32_t kMinDuration = 10;
const uint32_t kDefaultDuration = 10;

const int32_t kDefaultNumberOfBuffers = 4;
const int32_t kMaxNumberOfBuffers = 1000;  // 10 sec

// Returns true if the |sample_rate| is supported in
// |PP_AudioBuffer_SampleRate|, otherwise false.
PP_AudioBuffer_SampleRate GetPPSampleRate(int sample_rate) {
  switch (sample_rate) {
    case 8000:
      return PP_AUDIOBUFFER_SAMPLERATE_8000;
    case 16000:
      return PP_AUDIOBUFFER_SAMPLERATE_16000;
    case 22050:
      return PP_AUDIOBUFFER_SAMPLERATE_22050;
    case 32000:
      return PP_AUDIOBUFFER_SAMPLERATE_32000;
    case 44100:
      return PP_AUDIOBUFFER_SAMPLERATE_44100;
    case 48000:
      return PP_AUDIOBUFFER_SAMPLERATE_48000;
    case 96000:
      return PP_AUDIOBUFFER_SAMPLERATE_96000;
    case 192000:
      return PP_AUDIOBUFFER_SAMPLERATE_192000;
    default:
      return PP_AUDIOBUFFER_SAMPLERATE_UNKNOWN;
  }
}

}  // namespace

namespace content {

PepperMediaStreamAudioTrackHost::AudioSink::AudioSink(
    PepperMediaStreamAudioTrackHost* host)
    : host_(host),
      active_buffer_index_(-1),
      active_buffers_generation_(0),
      active_buffer_frame_offset_(0),
      buffers_generation_(0),
      main_message_loop_proxy_(base::MessageLoopProxy::current()),
      number_of_buffers_(kDefaultNumberOfBuffers),
      bytes_per_second_(0),
      bytes_per_frame_(0),
      user_buffer_duration_(kDefaultDuration),
      weak_factory_(this) {}

PepperMediaStreamAudioTrackHost::AudioSink::~AudioSink() {
  DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
}

void PepperMediaStreamAudioTrackHost::AudioSink::EnqueueBuffer(int32_t index) {
  DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
  DCHECK_GE(index, 0);
  DCHECK_LT(index, host_->buffer_manager()->number_of_buffers());
  base::AutoLock lock(lock_);
  buffers_.push_back(index);
}

int32_t PepperMediaStreamAudioTrackHost::AudioSink::Configure(
    int32_t number_of_buffers, int32_t duration,
    const ppapi::host::ReplyMessageContext& context) {
  DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());

  if (pending_configure_reply_.is_valid()) {
    return PP_ERROR_INPROGRESS;
  }
  pending_configure_reply_ = context;

  bool changed = false;
  if (number_of_buffers != number_of_buffers_)
    changed = true;
  if (duration != 0 && duration != user_buffer_duration_) {
    user_buffer_duration_ = duration;
    changed = true;
  }
  number_of_buffers_ = number_of_buffers;

  if (changed) {
    // Initialize later in OnSetFormat if bytes_per_second_ is not known yet.
    if (bytes_per_second_ > 0 && bytes_per_frame_ > 0)
      InitBuffers();
  } else {
    SendConfigureReply(PP_OK);
  }
  return PP_OK_COMPLETIONPENDING;
}

void PepperMediaStreamAudioTrackHost::AudioSink::SendConfigureReply(
    int32_t result) {
  if (pending_configure_reply_.is_valid()) {
    pending_configure_reply_.params.set_result(result);
    host_->host()->SendReply(
        pending_configure_reply_,
        PpapiPluginMsg_MediaStreamAudioTrack_ConfigureReply());
    pending_configure_reply_ = ppapi::host::ReplyMessageContext();
  }
}

void PepperMediaStreamAudioTrackHost::AudioSink::SetFormatOnMainThread(
    int bytes_per_second, int bytes_per_frame) {
  bytes_per_second_ = bytes_per_second;
  bytes_per_frame_ = bytes_per_frame;
  InitBuffers();
}

void PepperMediaStreamAudioTrackHost::AudioSink::InitBuffers() {
  DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
  {
    base::AutoLock lock(lock_);
    // Clear |buffers_|, so the audio thread will drop all incoming audio data.
    buffers_.clear();
    buffers_generation_++;
  }
  int32_t frame_rate = bytes_per_second_ / bytes_per_frame_;
  base::CheckedNumeric<int32_t> frames_per_buffer = user_buffer_duration_;
  frames_per_buffer *= frame_rate;
  frames_per_buffer /= base::Time::kMillisecondsPerSecond;
  base::CheckedNumeric<int32_t> buffer_audio_size =
      frames_per_buffer * bytes_per_frame_;
  // The size is slightly bigger than necessary, because 8 extra bytes are
  // added into the struct. Also see |MediaStreamBuffer|. Also, the size of the
  // buffer may be larger than requested, since the size of each buffer will be
  // 4-byte aligned.
  base::CheckedNumeric<int32_t> buffer_size = buffer_audio_size;
  buffer_size += sizeof(ppapi::MediaStreamBuffer::Audio);
  DCHECK_GT(buffer_size.ValueOrDie(), 0);

  // We don't need to hold |lock_| during |host->InitBuffers()| call, because
  // we just cleared |buffers_| , so the audio thread will drop all incoming
  // audio data, and not use buffers in |host_|.
  bool result = host_->InitBuffers(number_of_buffers_,
                                   buffer_size.ValueOrDie(),
                                   kRead);
  if (!result) {
    SendConfigureReply(PP_ERROR_NOMEMORY);
    return;
  }

  // Fill the |buffers_|, so the audio thread can continue receiving audio data.
  base::AutoLock lock(lock_);
  output_buffer_size_ = buffer_audio_size.ValueOrDie();
  for (int32_t i = 0; i < number_of_buffers_; ++i) {
    int32_t index = host_->buffer_manager()->DequeueBuffer();
    DCHECK_GE(index, 0);
    buffers_.push_back(index);
  }

  SendConfigureReply(PP_OK);
}

void PepperMediaStreamAudioTrackHost::AudioSink::
    SendEnqueueBufferMessageOnMainThread(int32_t index,
                                         int32_t buffers_generation) {
  DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
  // If |InitBuffers()| is called after this task being posted from the audio
  // thread, the buffer should become invalid already. We should ignore it.
  // And because only the main thread modifies the |buffers_generation_|,
  // so we don't need to lock |lock_| here (main thread).
  if (buffers_generation == buffers_generation_)
    host_->SendEnqueueBufferMessageToPlugin(index);
}

void PepperMediaStreamAudioTrackHost::AudioSink::OnData(
    const media::AudioBus& audio_bus,
    base::TimeTicks estimated_capture_time) {
  DCHECK(audio_thread_checker_.CalledOnValidThread());
  DCHECK(audio_params_.IsValid());
  DCHECK_EQ(audio_bus.channels(), audio_params_.channels());
  // Here, |audio_params_.frames_per_buffer()| refers to the incomming audio
  // buffer. However, this doesn't necessarily equal
  // |buffer->number_of_samples|, which is configured by the user when they set
  // buffer duration.
  DCHECK_EQ(audio_bus.frames(), audio_params_.frames_per_buffer());
  DCHECK(!estimated_capture_time.is_null());

  if (first_frame_capture_time_.is_null())
    first_frame_capture_time_ = estimated_capture_time;

  const int bytes_per_frame = audio_params_.GetBytesPerFrame();

  base::AutoLock lock(lock_);
  for (int frame_offset = 0; frame_offset < audio_bus.frames(); ) {
    if (active_buffers_generation_ != buffers_generation_) {
      // Buffers have changed, so drop the active buffer.
      active_buffer_index_ = -1;
    }
    if (active_buffer_index_ == -1 && !buffers_.empty()) {
      active_buffers_generation_ = buffers_generation_;
      active_buffer_frame_offset_ = 0;
      active_buffer_index_ = buffers_.front();
      buffers_.pop_front();
    }
    if (active_buffer_index_ == -1) {
      // Eek! We're dropping frames. Bad, bad, bad!
      break;
    }

    // TODO(penghuang): Support re-sampling and channel mixing by using
    // media::AudioConverter.
    ppapi::MediaStreamBuffer::Audio* buffer =
        &(host_->buffer_manager()->GetBufferPointer(active_buffer_index_)
          ->audio);
    if (active_buffer_frame_offset_ == 0) {
      // The active buffer is new, so initialise the header and metadata fields.
      buffer->header.size = host_->buffer_manager()->buffer_size();
      buffer->header.type = ppapi::MediaStreamBuffer::TYPE_AUDIO;
      const base::TimeTicks time_at_offset = estimated_capture_time +
          frame_offset * base::TimeDelta::FromSeconds(1) /
              audio_params_.sample_rate();
      buffer->timestamp =
          (time_at_offset - first_frame_capture_time_).InSecondsF();
      buffer->sample_rate =
          static_cast<PP_AudioBuffer_SampleRate>(audio_params_.sample_rate());
      buffer->data_size = output_buffer_size_;
      buffer->number_of_channels = audio_params_.channels();
      buffer->number_of_samples = buffer->data_size * audio_params_.channels() /
          bytes_per_frame;
    }

    const int frames_per_buffer =
        buffer->number_of_samples / audio_params_.channels();
    const int frames_to_copy = std::min(
        frames_per_buffer - active_buffer_frame_offset_,
        audio_bus.frames() - frame_offset);
    audio_bus.ToInterleavedPartial(
        frame_offset,
        frames_to_copy,
        audio_params_.bits_per_sample() / 8,
        buffer->data + active_buffer_frame_offset_ * bytes_per_frame);
    active_buffer_frame_offset_ += frames_to_copy;
    frame_offset += frames_to_copy;

    DCHECK_LE(active_buffer_frame_offset_, frames_per_buffer);
    if (active_buffer_frame_offset_ == frames_per_buffer) {
      main_message_loop_proxy_->PostTask(
          FROM_HERE,
          base::Bind(&AudioSink::SendEnqueueBufferMessageOnMainThread,
                     weak_factory_.GetWeakPtr(),
                     active_buffer_index_,
                     buffers_generation_));
      active_buffer_index_ = -1;
    }
  }
}

void PepperMediaStreamAudioTrackHost::AudioSink::OnSetFormat(
    const AudioParameters& params) {
  DCHECK(params.IsValid());
  // TODO(amistry): How do you handle the case where the user configures a
  // duration that's shorter than the received buffer duration? One option is to
  // double buffer, where the size of the intermediate ring buffer is at least
  // max(user requested duration, received buffer duration). There are other
  // ways of dealing with it, but which one is "correct"?
  DCHECK_LE(params.GetBufferDuration().InMilliseconds(), kMinDuration);
  DCHECK_EQ(params.bits_per_sample(), 16);
  DCHECK_NE(GetPPSampleRate(params.sample_rate()),
            PP_AUDIOBUFFER_SAMPLERATE_UNKNOWN);

  // TODO(penghuang): support setting format more than once.
  if (audio_params_.IsValid()) {
    DCHECK_EQ(params.sample_rate(), audio_params_.sample_rate());
    DCHECK_EQ(params.bits_per_sample(), audio_params_.bits_per_sample());
    DCHECK_EQ(params.channels(), audio_params_.channels());
  } else {
    audio_thread_checker_.DetachFromThread();
    audio_params_ = params;

    main_message_loop_proxy_->PostTask(
        FROM_HERE,
        base::Bind(&AudioSink::SetFormatOnMainThread,
                   weak_factory_.GetWeakPtr(),
                   params.GetBytesPerSecond(),
                   params.GetBytesPerFrame()));
  }
}

PepperMediaStreamAudioTrackHost::PepperMediaStreamAudioTrackHost(
    RendererPpapiHost* host,
    PP_Instance instance,
    PP_Resource resource,
    const blink::WebMediaStreamTrack& track)
    : PepperMediaStreamTrackHostBase(host, instance, resource),
      track_(track),
      connected_(false),
      audio_sink_(this) {
  DCHECK(!track_.isNull());
}

PepperMediaStreamAudioTrackHost::~PepperMediaStreamAudioTrackHost() {
  OnClose();
}

int32_t PepperMediaStreamAudioTrackHost::OnResourceMessageReceived(
    const IPC::Message& msg,
    HostMessageContext* context) {
  PPAPI_BEGIN_MESSAGE_MAP(PepperMediaStreamAudioTrackHost, msg)
    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
        PpapiHostMsg_MediaStreamAudioTrack_Configure, OnHostMsgConfigure)
  PPAPI_END_MESSAGE_MAP()
  return PepperMediaStreamTrackHostBase::OnResourceMessageReceived(msg,
                                                                   context);
}

int32_t PepperMediaStreamAudioTrackHost::OnHostMsgConfigure(
    HostMessageContext* context,
    const MediaStreamAudioTrackShared::Attributes& attributes) {
  if (!MediaStreamAudioTrackShared::VerifyAttributes(attributes))
    return PP_ERROR_BADARGUMENT;

  int32_t buffers = attributes.buffers
                        ? std::min(kMaxNumberOfBuffers, attributes.buffers)
                        : kDefaultNumberOfBuffers;
  return audio_sink_.Configure(buffers, attributes.duration,
                               context->MakeReplyMessageContext());
}

void PepperMediaStreamAudioTrackHost::OnClose() {
  if (connected_) {
    MediaStreamAudioSink::RemoveFromAudioTrack(&audio_sink_, track_);
    connected_ = false;
  }
  audio_sink_.SendConfigureReply(PP_ERROR_ABORTED);
}

void PepperMediaStreamAudioTrackHost::OnNewBufferEnqueued() {
  int32_t index = buffer_manager()->DequeueBuffer();
  DCHECK_GE(index, 0);
  audio_sink_.EnqueueBuffer(index);
}

void PepperMediaStreamAudioTrackHost::DidConnectPendingHostToResource() {
  if (!connected_) {
    media::AudioParameters format =
        MediaStreamAudioSink::GetFormatFromAudioTrack(track_);
    // Although this should only be called on the audio capture thread, that
    // can't happen until the sink is added to the audio track below.
    if (format.IsValid())
      audio_sink_.OnSetFormat(format);

    MediaStreamAudioSink::AddToAudioTrack(&audio_sink_, track_);
    connected_ = true;
  }
}

}  // namespace content
<|endoftext|>


<commit_before>/* BSD 3-Clause License:
 * Copyright (c) 2018, bitsofcotton.
 * 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 or other materials provided with the distribution.
 *    Neither 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 HOLDER 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.
 */

#if !defined(_2D3D_PSEUDO_)

#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
#include <Eigen/Core>
#include <Eigen/Dense>

using std::complex;
using std::abs;
using std::vector;
using std::sort;
using std::max;
using std::min;
using std::sqrt;
using std::exp;
using std::pow;
using std::isfinite;
using std::cerr;
using std::flush;
using std::vector;

template <typename T> class PseudoBump {
public:
  typedef complex<T> U;
  typedef Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> Mat;
  typedef Eigen::Matrix<T, Eigen::Dynamic, 1>              Vec;
  typedef Eigen::Matrix<U, Eigen::Dynamic, Eigen::Dynamic> MatU;
  typedef Eigen::Matrix<T,   3, 1> Vec3;
  typedef Eigen::Matrix<int, 3, 1> Veci3;

  PseudoBump();
  ~PseudoBump();
  void initialize(const int& z_max, const int& stp);
  void getPseudoVec(const Mat& in, vector<Vec3>& geoms, vector<Veci3>& delaunay, const int& vbox = 4, const T& rz = T(1) / T(6));
  Mat  getPseudoBump(const Mat& in);

private:
  Vec         getPseudoBumpSub(const Vec& work, const vector<Vec>& cf);
  const T&    getImgPt(const Vec& img, const T& y);
  vector<Vec> prepareLineAxis(const T& rstp);

  T   Pi;

  int z_max;
  Vec Dop;
  Vec DDDop;
};

template <typename T> PseudoBump<T>::PseudoBump() {
  initialize(12, 201);
}

template <typename T> PseudoBump<T>::~PseudoBump() {
  ;
}

template <typename T> void PseudoBump<T>::initialize(const int& z_max, const int& stp) {
  this->z_max  = z_max;
  assert(1 < z_max && 0 < stp);
  this->Pi     = T(4) * atan2(T(1), T(1));
  MatU DFT(stp, stp), IDFT(stp, stp);
  for(int i = 0; i < DFT.rows(); i ++)
    for(int j = 0; j < DFT.cols(); j ++) {
      DFT( i, j) = exp(U(- 2.) * Pi * sqrt(U(- 1)) * U(i * j / T(stp)));
      IDFT(i, j) = exp(U(  2.) * Pi * sqrt(U(- 1)) * U(i * j / T(stp))) / T(stp);
    }
  auto DDDFT(DFT);
  for(int i = 0; i < DFT.rows(); i ++) {
    const U rtheta(U(2.) * Pi * sqrt(U(- 1)) * T(i) / T(DFT.rows()));
    DFT.row(i)   *= rtheta;
    DDDFT.row(i) *= rtheta * rtheta * rtheta;
  }
  Dop    = (IDFT *   DFT).row(IDFT.rows() / 2).real().template cast<T>();
  DDDop  = (IDFT * DDDFT).row(IDFT.rows() / 2).real().template cast<T>();
  Dop   /= sqrt(  Dop.dot(  Dop));
  DDDop /= sqrt(DDDop.dot(DDDop));
  return;
}

template <typename T> Eigen::Matrix<T, Eigen::Dynamic, 1> PseudoBump<T>::getPseudoBumpSub(const Vec& work, const vector<Vec>& cf) {
  cerr << "." << flush;
  Vec result(work.size());
  for(int s = 0; s < work.size(); s ++) {
    T mbuf(2);
    result[s] = T(0);
    for(int z = 0; z < cf.size(); z ++) {
      // d/dt (local color) / ||local color||:
      Vec c(cf[z].size());
      for(int u = 0; u < c.size(); u ++)
        c[u] = getImgPt(work, cf[z][u] + s);
      const T score(abs(Dop.dot(c) * DDDop.dot(c)) / c.dot(c) / c.dot(c));
      if(score < mbuf) {
        result[s] = z / T(cf.size());
        mbuf      = score;
      }
    }
  }
  return result;
}

template <typename T> Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> PseudoBump<T>::getPseudoBump(const Mat& in) {
  auto cf(prepareLineAxis(sqrt(T(in.rows() * in.cols()))));
  Mat result(in.rows(), in.cols());
#if defined(_OPENMP)
#pragma omp parallel for schedule(static, 1)
#endif
  for(int i = 0; i < result.cols(); i ++)
    result.col(i)  = getPseudoBumpSub(in.col(i), cf);
  // N.B. cross bump, if you don't need this, please comment out.
#if defined(_OPENMP)
#pragma omp parallel for schedule(static, 1)
#endif
  for(int i = 0; i < result.rows(); i ++)
    result.row(i) += getPseudoBumpSub(in.row(i), cf);
  return - result;
}

// get bump with multiple scale and vectorized result.
template <typename T> void PseudoBump<T>::getPseudoVec(const Mat& in, vector<Vec3>& geoms, vector<Veci3>& delaunay, const int& vbox, const T& rz) {
  // get vectorize.
  geoms = vector<Vec3>();
  T aavg(0);
  for(int i = 0; i < in.rows(); i ++)
    for(int j = 0; j < in.cols(); j ++)
      aavg += in(i, j);
  aavg /= in.rows() * in.cols();
  for(int i = 0; i < in.rows() / vbox + 1; i ++)
    for(int j = 0; j < in.cols() / vbox + 1; j ++) {
      if(in.rows() < (i + 1) * vbox ||
         in.cols() < (j + 1) * vbox) {
        if(geoms.size() >= 1) {
          Vec3 gbuf;
          gbuf[0] = i * vbox;
          gbuf[1] = j * vbox;
          gbuf[2] = geoms[geoms.size() - 1][2];
          geoms.push_back(gbuf);
        }
        continue;
      }
      T avg(0);
      for(int ii = i * vbox; ii < (i + 1) * vbox; ii ++)
        for(int jj = j * vbox; jj < (j + 1) * vbox; jj ++)
          avg += in(ii, jj);
      Vec3 work;
      work[0] = i * vbox;
      work[1] = j * vbox;
      const T intens(avg / vbox / vbox - aavg);
      work[2] = - intens * sqrt(T(in.rows() * in.cols())) * rz;
      geoms.push_back(work);
    }
  Vec3 avg;
  avg[0] = avg[1] = avg[2] = T(0);
  for(int i = 0; i < geoms.size(); i ++)
    avg += geoms[i];
  avg /= geoms.size();
  for(int i = 0; i < geoms.size(); i ++)
    geoms[i][2] -= avg[2];
  delaunay = vector<Veci3>();
  for(int i = 1; i < in.rows() / vbox + 1; i ++)
    for(int j = 0; j < in.cols() / vbox; j ++) {
      Veci3 work, work2;
      work[0]  = (i - 1) * (in.cols() / vbox + 1) + j;
      work[1]  =  i      * (in.cols() / vbox + 1) + j;
      work[2]  =  i      * (in.cols() / vbox + 1) + j + 1;
      work2[0] = (i - 1) * (in.cols() / vbox + 1) + j;
      work2[2] = (i - 1) * (in.cols() / vbox + 1) + j + 1;
      work2[1] =  i      * (in.cols() / vbox + 1) + j + 1;
      delaunay.push_back(work);
      delaunay.push_back(work2);
    }
  return;
}

template <typename T> vector<Eigen::Matrix<T, Eigen::Dynamic, 1> > PseudoBump<T>::prepareLineAxis(const T& rstp) {
  // N.B. ray is from infinite far, so same side of these.
  Vec camera(2);
  camera[0] = T(0);
  camera[1] = T(1);

  vector<Vec> result;
  result.resize(z_max);
#if defined(_OPENMP)
#pragma omp parallel for schedule(static, 1)
#endif
  for(int zi = 0; zi < result.size(); zi ++) {
    result[zi] = Vec(Dop.size());
    for(int s = 0; s < result[zi].size(); s ++) {
      Vec cpoint(2);
      cpoint[0] = (s / T(Dop.size() - 1) - 1 / T(2));
      cpoint[1] = (1 + zi) / T(Dop.size());
      // x-z plane projection of point p with camera geometry c to z=0.
      // c := camera, p := cpoint.
      // <c + (p - c) * t, [0, 1]> = 0
      const T t(- camera[1] / (cpoint[1] - camera[1]));
      result[zi][s] = (camera + (cpoint - camera) * t)[0] * rstp;
    }
  }
  return result;
}

template <typename T> const T& PseudoBump<T>::getImgPt(const Vec& img, const T& y) {
  const int& h(img.size());
  const int  yy(abs((int(y + .5) + 3 * h) % (2 * h) - h) % h);
  return img[yy];
}

#define _2D3D_PSEUDO_
#endif

<commit_msg>prepareLineAxis logical fix.<commit_after>/* BSD 3-Clause License:
 * Copyright (c) 2018, bitsofcotton.
 * 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 or other materials provided with the distribution.
 *    Neither 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 HOLDER 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.
 */

#if !defined(_2D3D_PSEUDO_)

#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
#include <Eigen/Core>
#include <Eigen/Dense>

using std::complex;
using std::abs;
using std::vector;
using std::sort;
using std::max;
using std::min;
using std::sqrt;
using std::exp;
using std::pow;
using std::isfinite;
using std::cerr;
using std::flush;
using std::vector;

template <typename T> class PseudoBump {
public:
  typedef complex<T> U;
  typedef Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> Mat;
  typedef Eigen::Matrix<T, Eigen::Dynamic, 1>              Vec;
  typedef Eigen::Matrix<U, Eigen::Dynamic, Eigen::Dynamic> MatU;
  typedef Eigen::Matrix<T,   3, 1> Vec3;
  typedef Eigen::Matrix<int, 3, 1> Veci3;

  PseudoBump();
  ~PseudoBump();
  void initialize(const int& z_max, const int& stp, const T& thresh);
  void getPseudoVec(const Mat& in, vector<Vec3>& geoms, vector<Veci3>& delaunay, const int& vbox = 4, const T& rz = T(1) / T(6));
  Mat  getPseudoBump(const Mat& in);

private:
  Vec         getPseudoBumpSub(const Vec& work, const vector<Vec>& cf);
  const T&    getImgPt(const Vec& img, const T& y);
  vector<Vec> prepareLineAxis(const T& rstp);

  T   Pi;

  T   thresh;
  int z_max;
  Vec Dop;
  Vec DDDop;
};

template <typename T> PseudoBump<T>::PseudoBump() {
  initialize(12, 51, T(.05));
}

template <typename T> PseudoBump<T>::~PseudoBump() {
  ;
}

template <typename T> void PseudoBump<T>::initialize(const int& z_max, const int& stp, const T& thresh) {
  this->z_max  = z_max;
  this->thresh = thresh;
  assert(1 < z_max && 0 < stp && T(0) <= thresh);
  this->Pi     = T(4) * atan2(T(1), T(1));
  MatU DFT(stp, stp), IDFT(stp, stp);
  for(int i = 0; i < DFT.rows(); i ++)
    for(int j = 0; j < DFT.cols(); j ++) {
      DFT( i, j) = exp(U(- 2.) * Pi * sqrt(U(- 1)) * U(i * j / T(stp)));
      IDFT(i, j) = exp(U(  2.) * Pi * sqrt(U(- 1)) * U(i * j / T(stp))) / T(stp);
    }
  auto DDDFT(DFT);
  for(int i = 0; i < DFT.rows(); i ++) {
    const U rtheta(U(2.) * Pi * sqrt(U(- 1)) * T(i) / T(DFT.rows()));
    DFT.row(i)   *= rtheta;
    DDDFT.row(i) *= rtheta * rtheta * rtheta;
  }
  Dop    = (IDFT *   DFT).row(IDFT.rows() / 2).real().template cast<T>();
  DDDop  = (IDFT * DDDFT).row(IDFT.rows() / 2).real().template cast<T>();
  Dop   /= sqrt(  Dop.dot(  Dop));
  DDDop /= sqrt(DDDop.dot(DDDop));
  return;
}

template <typename T> Eigen::Matrix<T, Eigen::Dynamic, 1> PseudoBump<T>::getPseudoBumpSub(const Vec& work, const vector<Vec>& cf) {
  cerr << "." << flush;
  Vec result(work.size());
  for(int s = 0; s < work.size(); s ++) {
    T buf(2);
    T mbuf(2);
    result[s] = T(1);
    for(int z = 0; z < cf.size(); z ++) {
      // d/dt (local color) / ||local color||:
      Vec c(cf[z].size());
      for(int u = 0; u < c.size(); u ++)
        c[u] = getImgPt(work, cf[z][u] + s);
      const T score(abs(Dop.dot(c) * DDDop.dot(c)) / c.dot(c));
      if(score < mbuf) {
        result[s] = z / T(cf.size());
        mbuf      = score;
      // N.B. cf[z] increases width of differences, so if it rapidly increases,
      //      it's on edge point.
      } else if(buf * (T(1) + thresh) < score)
        break;
      buf = score;
    }
  }
  return result;
}

template <typename T> Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> PseudoBump<T>::getPseudoBump(const Mat& in) {
  auto cf(prepareLineAxis(sqrt(T(in.rows() * in.cols()))));
  Mat result(in.rows(), in.cols());
#if defined(_OPENMP)
#pragma omp parallel for schedule(static, 1)
#endif
  for(int i = 0; i < result.cols(); i ++)
    result.col(i)  = getPseudoBumpSub(in.col(i), cf);
  // N.B. cross bump, if you don't need this, please comment out.
#if defined(_OPENMP)
#pragma omp parallel for schedule(static, 1)
#endif
  for(int i = 0; i < result.rows(); i ++)
    result.row(i) += getPseudoBumpSub(in.row(i), cf);
  return - result;
}

// get bump with multiple scale and vectorized result.
template <typename T> void PseudoBump<T>::getPseudoVec(const Mat& in, vector<Vec3>& geoms, vector<Veci3>& delaunay, const int& vbox, const T& rz) {
  // get vectorize.
  geoms = vector<Vec3>();
  T aavg(0);
  for(int i = 0; i < in.rows(); i ++)
    for(int j = 0; j < in.cols(); j ++)
      aavg += in(i, j);
  aavg /= in.rows() * in.cols();
  for(int i = 0; i < in.rows() / vbox + 1; i ++)
    for(int j = 0; j < in.cols() / vbox + 1; j ++) {
      if(in.rows() < (i + 1) * vbox ||
         in.cols() < (j + 1) * vbox) {
        if(geoms.size() >= 1) {
          Vec3 gbuf;
          gbuf[0] = i * vbox;
          gbuf[1] = j * vbox;
          gbuf[2] = geoms[geoms.size() - 1][2];
          geoms.push_back(gbuf);
        }
        continue;
      }
      T avg(0);
      for(int ii = i * vbox; ii < (i + 1) * vbox; ii ++)
        for(int jj = j * vbox; jj < (j + 1) * vbox; jj ++)
          avg += in(ii, jj);
      Vec3 work;
      work[0] = i * vbox;
      work[1] = j * vbox;
      const T intens(avg / vbox / vbox - aavg);
      work[2] = - intens * sqrt(T(in.rows() * in.cols())) * rz;
      geoms.push_back(work);
    }
  Vec3 avg;
  avg[0] = avg[1] = avg[2] = T(0);
  for(int i = 0; i < geoms.size(); i ++)
    avg += geoms[i];
  avg /= geoms.size();
  for(int i = 0; i < geoms.size(); i ++)
    geoms[i][2] -= avg[2];
  delaunay = vector<Veci3>();
  for(int i = 1; i < in.rows() / vbox + 1; i ++)
    for(int j = 0; j < in.cols() / vbox; j ++) {
      Veci3 work, work2;
      work[0]  = (i - 1) * (in.cols() / vbox + 1) + j;
      work[1]  =  i      * (in.cols() / vbox + 1) + j;
      work[2]  =  i      * (in.cols() / vbox + 1) + j + 1;
      work2[0] = (i - 1) * (in.cols() / vbox + 1) + j;
      work2[2] = (i - 1) * (in.cols() / vbox + 1) + j + 1;
      work2[1] =  i      * (in.cols() / vbox + 1) + j + 1;
      delaunay.push_back(work);
      delaunay.push_back(work2);
    }
  return;
}

template <typename T> vector<Eigen::Matrix<T, Eigen::Dynamic, 1> > PseudoBump<T>::prepareLineAxis(const T& rstp) {
  // N.B. ray is from infinite far, so same side of these.
  Vec camera(2);
  camera[0] = T(0);
  camera[1] = T(1);

  vector<Vec> result;
  result.resize(z_max, Vec(Dop.size()));
#if defined(_OPENMP)
#pragma omp parallel for schedule(static, 1)
#endif
  for(int zi = 0; zi < result.size(); zi ++) {
    for(int s = 0; s < result[zi].size(); s ++) {
      Vec cpoint(2);
      cpoint[0] = (s / T(Dop.size() - 1) - 1 / T(2));
      // [0, 1] not works well, [1, infty[ works better.
      cpoint[1] = 1.5 + zi;
      // x-z plane projection of point p with camera geometry c to z=0.
      // c := camera, p := cpoint.
      // <c + (p - c) * t, [0, 1]> = 0
      const T t(- camera[1] / (cpoint[1] - camera[1]));
      // result increases near to far on differential points.
      result[result.size() - 1 - zi][s] = (camera + (cpoint - camera) * t)[0] * rstp;
    }
  }
  return result;
}

template <typename T> const T& PseudoBump<T>::getImgPt(const Vec& img, const T& y) {
  const int& h(img.size());
  const int  yy(abs((int(y + .5) + 3 * h) % (2 * h) - h) % h);
  return img[yy];
}

#define _2D3D_PSEUDO_
#endif

<|endoftext|>