<commit_before>/****************************************************************************
**
** This file is part of QtCompositor**
**
** Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact:  Nokia Corporation qt-info@nokia.com
**
** You may use this file under the terms of the BSD license as follows:
**
** 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 and/or other materials provided with the distribution.
**
** Neither the name of Nokia Corporation and its Subsidiary(-ies) nor 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
** OWNER 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.
**
****************************************************************************/

#include "wlselection.h"
#include "wlcompositor.h"
#include <wayland-util.h>
#include <string.h>
#include <unistd.h>
#include <QtCore/QFile>
#include <QtCore/QSocketNotifier>

namespace Wayland {

void Selection::send(struct wl_client *client,
                     struct wl_selection_offer *offer,
                     const char *mime_type, int fd)
{
    Q_UNUSED(client);
    Selection *self = instance();
    if (self->m_retainedSelection) {
        QByteArray data = self->m_retainedData.data(QString::fromLatin1(mime_type));
        if (!data.isEmpty()) {
            QFile f;
            if (f.open(fd, QIODevice::WriteOnly))
                f.write(data);
        }
    } else {
        struct wl_selection *selection = container_of(offer, struct wl_selection, selection_offer);
        wl_client_post_event(selection->client,
                             &selection->resource.object,
                             WL_SELECTION_SEND, mime_type, fd);
    }
    close(fd);
}

const struct wl_selection_offer_interface Selection::selectionOfferInterface = {
    Selection::send
};

void Selection::selOffer(struct wl_client *client,
                         struct wl_selection *selection,
                         const char *type)
{
    Q_UNUSED(client);
    Q_UNUSED(selection);
    instance()->m_offerList.append(QString::fromLatin1(type));
}

void Selection::selActivate(struct wl_client *client,
                            struct wl_selection *selection,
                            struct wl_input_device *device,
                            uint32_t time)
{
    Q_UNUSED(client);
    Q_UNUSED(device);
    Q_UNUSED(time);
    Selection *self = Selection::instance();

    selection->selection_offer.object.interface = &wl_selection_offer_interface;
    selection->selection_offer.object.implementation = (void (**)()) &selectionOfferInterface;
    wl_display *dpy = Compositor::instance()->wl_display();
    wl_display_add_object(dpy, &selection->selection_offer.object);
    wl_display_add_global(dpy, &selection->selection_offer.object, 0);

    QList<struct wl_client *> clients = Compositor::instance()->clients();
    if (self->m_currentSelection) {
        if (!clients.contains(self->m_currentSelection->client))
            self->m_currentSelection = 0;
        else
            wl_client_post_event(self->m_currentSelection->client,
                                 &self->m_currentSelection->resource.object,
                                 WL_SELECTION_CANCELLED);
    }
    self->m_currentSelection = selection;

    if (self->m_currentOffer) {
        foreach (struct wl_client *client, clients) {
            wl_client_post_event(client, &self->m_currentOffer->object,
                                 WL_SELECTION_OFFER_KEYBOARD_FOCUS, 0);
        }
    }
    self->m_currentOffer = &selection->selection_offer;
    foreach (struct wl_client *client, clients) {
        wl_client_post_global(client, &selection->selection_offer.object);
    }
    foreach (struct wl_client *client, clients) {
        foreach (const QString &mimeType, self->m_offerList) {
            QByteArray mimeTypeBa = mimeType.toLatin1();
            wl_client_post_event(client, &selection->selection_offer.object,
                                 WL_SELECTION_OFFER_OFFER, mimeTypeBa.constData());
        }
    }
    foreach (struct wl_client *client, clients) {
        wl_client_post_event(client, &selection->selection_offer.object,
                             WL_SELECTION_OFFER_KEYBOARD_FOCUS, selection->input_device);
    }

    if (self->m_retainedSelectionEnabled) {
        self->m_retainedData.clear();
        self->m_retainedReadIndex = 0;
        self->retain();
    }
}

void Selection::retain()
{
    finishReadFromClient();
    if (m_retainedReadIndex >= m_offerList.count()) {
        if (m_watchFunc)
            m_watchFunc(&m_retainedData, m_watchFuncParam);
        return;
    }
    QString mimeType = m_offerList.at(m_retainedReadIndex);
    m_retainedReadBuf.clear();
    QByteArray mimeTypeBa = mimeType.toLatin1();
    int fd[2];
    if (pipe(fd) == -1) {
        qWarning("Clipboard: Failed to create pipe");
        return;
    }
    wl_client_post_event(m_currentSelection->client, &m_currentSelection->resource.object,
                         WL_SELECTION_SEND, mimeTypeBa.constData(), fd[1]);
    close(fd[1]);
    m_retainedReadNotifier = new QSocketNotifier(fd[0], QSocketNotifier::Read, this);
    connect(m_retainedReadNotifier, SIGNAL(activated(int)), SLOT(readFromClient(int)));
}

void Selection::finishReadFromClient()
{
    if (m_retainedReadNotifier) {
        int fd = m_retainedReadNotifier->socket();
        delete m_retainedReadNotifier;
        m_retainedReadNotifier = 0;
        close(fd);
    }
}

void Selection::readFromClient(int fd)
{
    char buf[256];
    int n = read(fd, buf, sizeof buf);
    if (n <= 0) {
        finishReadFromClient();
        QString mimeType = m_offerList.at(m_retainedReadIndex);
        m_retainedData.setData(mimeType, m_retainedReadBuf);
        ++m_retainedReadIndex;
        retain();
    } else {
        m_retainedReadBuf.append(buf, n);
    }
}

void Selection::selDestroy(struct wl_client *client, struct wl_selection *selection)
{
    wl_resource_destroy(&selection->resource, client, Compositor::currentTimeMsecs());
}

const struct wl_selection_interface Selection::selectionInterface = {
    Selection::selOffer,
    Selection::selActivate,
    Selection::selDestroy
};

void Selection::destroySelection(struct wl_resource *resource, struct wl_client *client)
{
    Q_UNUSED(client);
    struct wl_selection *selection = container_of(resource, struct wl_selection, resource);
    Selection *self = Selection::instance();
    wl_display *dpy = Compositor::instance()->wl_display();
    if (self->m_currentSelection == selection)
        self->m_currentSelection = 0;
    if (self->m_currentOffer == &selection->selection_offer) {
        self->m_currentOffer = 0;
        if (self->m_retainedSelectionEnabled) {
            if (self->m_retainedSelection) {
                wl_display_remove_global(dpy, &self->m_retainedSelection->selection_offer.object);
                delete self->m_retainedSelection;
            }
            self->m_retainedSelection = selection;
            return;
        }
        self->m_offerList.clear();
        foreach (struct wl_client *client, Compositor::instance()->clients())
            wl_client_post_event(client, &selection->selection_offer.object,
                                 WL_SELECTION_OFFER_KEYBOARD_FOCUS, 0);
    }
    wl_display_remove_global(dpy, &selection->selection_offer.object);
    delete selection;
}

void Selection::create(struct wl_client *client, uint32_t id)
{
    delete m_retainedSelection;
    m_retainedSelection = 0;
    m_offerList.clear();
    struct wl_selection *selection = new struct wl_selection;
    memset(selection, 0, sizeof *selection);
    selection->resource.object.id = id;
    selection->resource.object.interface = &wl_selection_interface;
    selection->resource.object.implementation = (void (**)()) &selectionInterface;
    selection->resource.destroy = destroySelection;
    selection->client = client;
    selection->input_device = Compositor::instance()->inputDevice();
    wl_client_add_resource(client, &selection->resource);
}

void Selection::setRetainedSelection(bool enable)
{
    m_retainedSelectionEnabled = enable;
}

void Selection::setRetainedSelectionWatcher(Watcher func, void *param)
{
    m_watchFunc = func;
    m_watchFuncParam = param;
}

void Selection::onClientAdded(wl_client *client)
{
    struct wl_selection *selection = m_currentSelection;
    struct wl_selection_offer *offer = m_currentOffer;
    if (m_retainedSelection) {
        selection = m_retainedSelection;
        offer = &m_retainedSelection->selection_offer;
    }
    if (selection && offer) {
        wl_client_post_event(client, &offer->object,
                             WL_SELECTION_OFFER_KEYBOARD_FOCUS, 0);
        wl_client_post_global(client, &offer->object);
        foreach (const QString &mimeType, m_offerList) {
            QByteArray mimeTypeBa = mimeType.toLatin1();
            wl_client_post_event(client, &offer->object,
                                 WL_SELECTION_OFFER_OFFER, mimeTypeBa.constData());
        }
        wl_client_post_event(client, &offer->object,
                             WL_SELECTION_OFFER_KEYBOARD_FOCUS, selection->input_device);
    }
}

Q_GLOBAL_STATIC(Selection, globalInstance)

Selection *Selection::instance()
{
    return globalInstance();
}

Selection::Selection()
    : m_currentSelection(0), m_currentOffer(0),
      m_retainedReadNotifier(0), m_retainedSelection(0),
      m_retainedSelectionEnabled(false),
      m_watchFunc(0), m_watchFuncParam(0)
{
    connect(Compositor::instance(), SIGNAL(clientAdded(wl_client*)), SLOT(onClientAdded(wl_client*)));
}

Selection::~Selection()
{
    finishReadFromClient();
}

}
<commit_msg>Avoid post_global() whenever possible.<commit_after>/****************************************************************************
**
** This file is part of QtCompositor**
**
** Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact:  Nokia Corporation qt-info@nokia.com
**
** You may use this file under the terms of the BSD license as follows:
**
** 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 and/or other materials provided with the distribution.
**
** Neither the name of Nokia Corporation and its Subsidiary(-ies) nor 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
** OWNER 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.
**
****************************************************************************/

#include "wlselection.h"
#include "wlcompositor.h"
#include <wayland-util.h>
#include <string.h>
#include <unistd.h>
#include <QtCore/QFile>
#include <QtCore/QSocketNotifier>

namespace Wayland {

void Selection::send(struct wl_client *client,
                     struct wl_selection_offer *offer,
                     const char *mime_type, int fd)
{
    Q_UNUSED(client);
    Selection *self = instance();
    if (self->m_retainedSelection) {
        QByteArray data = self->m_retainedData.data(QString::fromLatin1(mime_type));
        if (!data.isEmpty()) {
            QFile f;
            if (f.open(fd, QIODevice::WriteOnly))
                f.write(data);
        }
    } else {
        struct wl_selection *selection = container_of(offer, struct wl_selection, selection_offer);
        wl_client_post_event(selection->client,
                             &selection->resource.object,
                             WL_SELECTION_SEND, mime_type, fd);
    }
    close(fd);
}

const struct wl_selection_offer_interface Selection::selectionOfferInterface = {
    Selection::send
};

void Selection::selOffer(struct wl_client *client,
                         struct wl_selection *selection,
                         const char *type)
{
    Q_UNUSED(client);
    Q_UNUSED(selection);
    instance()->m_offerList.append(QString::fromLatin1(type));
}

void Selection::selActivate(struct wl_client *client,
                            struct wl_selection *selection,
                            struct wl_input_device *device,
                            uint32_t time)
{
    Q_UNUSED(client);
    Q_UNUSED(device);
    Q_UNUSED(time);
    Selection *self = Selection::instance();

    selection->selection_offer.object.interface = &wl_selection_offer_interface;
    selection->selection_offer.object.implementation = (void (**)()) &selectionOfferInterface;
    wl_display *dpy = Compositor::instance()->wl_display();
    wl_display_add_object(dpy, &selection->selection_offer.object);
    wl_display_add_global(dpy, &selection->selection_offer.object, 0);

    QList<struct wl_client *> clients = Compositor::instance()->clients();
    if (self->m_currentSelection) {
        if (!clients.contains(self->m_currentSelection->client))
            self->m_currentSelection = 0;
        else
            wl_client_post_event(self->m_currentSelection->client,
                                 &self->m_currentSelection->resource.object,
                                 WL_SELECTION_CANCELLED);
    }
    self->m_currentSelection = selection;

    if (self->m_currentOffer) {
        foreach (struct wl_client *client, clients) {
            wl_client_post_event(client, &self->m_currentOffer->object,
                                 WL_SELECTION_OFFER_KEYBOARD_FOCUS, 0);
        }
    }
    self->m_currentOffer = &selection->selection_offer;
    foreach (struct wl_client *client, clients) {
        wl_client_post_global(client, &selection->selection_offer.object);
    }
    foreach (struct wl_client *client, clients) {
        foreach (const QString &mimeType, self->m_offerList) {
            QByteArray mimeTypeBa = mimeType.toLatin1();
            wl_client_post_event(client, &selection->selection_offer.object,
                                 WL_SELECTION_OFFER_OFFER, mimeTypeBa.constData());
        }
    }
    foreach (struct wl_client *client, clients) {
        wl_client_post_event(client, &selection->selection_offer.object,
                             WL_SELECTION_OFFER_KEYBOARD_FOCUS, selection->input_device);
    }

    if (self->m_retainedSelectionEnabled) {
        self->m_retainedData.clear();
        self->m_retainedReadIndex = 0;
        self->retain();
    }
}

void Selection::retain()
{
    finishReadFromClient();
    if (m_retainedReadIndex >= m_offerList.count()) {
        if (m_watchFunc)
            m_watchFunc(&m_retainedData, m_watchFuncParam);
        return;
    }
    QString mimeType = m_offerList.at(m_retainedReadIndex);
    m_retainedReadBuf.clear();
    QByteArray mimeTypeBa = mimeType.toLatin1();
    int fd[2];
    if (pipe(fd) == -1) {
        qWarning("Clipboard: Failed to create pipe");
        return;
    }
    wl_client_post_event(m_currentSelection->client, &m_currentSelection->resource.object,
                         WL_SELECTION_SEND, mimeTypeBa.constData(), fd[1]);
    close(fd[1]);
    m_retainedReadNotifier = new QSocketNotifier(fd[0], QSocketNotifier::Read, this);
    connect(m_retainedReadNotifier, SIGNAL(activated(int)), SLOT(readFromClient(int)));
}

void Selection::finishReadFromClient()
{
    if (m_retainedReadNotifier) {
        int fd = m_retainedReadNotifier->socket();
        delete m_retainedReadNotifier;
        m_retainedReadNotifier = 0;
        close(fd);
    }
}

void Selection::readFromClient(int fd)
{
    char buf[256];
    int n = read(fd, buf, sizeof buf);
    if (n <= 0) {
        finishReadFromClient();
        QString mimeType = m_offerList.at(m_retainedReadIndex);
        m_retainedData.setData(mimeType, m_retainedReadBuf);
        ++m_retainedReadIndex;
        retain();
    } else {
        m_retainedReadBuf.append(buf, n);
    }
}

void Selection::selDestroy(struct wl_client *client, struct wl_selection *selection)
{
    wl_resource_destroy(&selection->resource, client, Compositor::currentTimeMsecs());
}

const struct wl_selection_interface Selection::selectionInterface = {
    Selection::selOffer,
    Selection::selActivate,
    Selection::selDestroy
};

void Selection::destroySelection(struct wl_resource *resource, struct wl_client *client)
{
    Q_UNUSED(client);
    struct wl_selection *selection = container_of(resource, struct wl_selection, resource);
    Selection *self = Selection::instance();
    wl_display *dpy = Compositor::instance()->wl_display();
    if (self->m_currentSelection == selection)
        self->m_currentSelection = 0;
    if (self->m_currentOffer == &selection->selection_offer) {
        self->m_currentOffer = 0;
        if (self->m_retainedSelectionEnabled) {
            if (self->m_retainedSelection) {
                wl_display_remove_global(dpy, &self->m_retainedSelection->selection_offer.object);
                delete self->m_retainedSelection;
            }
            self->m_retainedSelection = selection;
            return;
        }
        self->m_offerList.clear();
        foreach (struct wl_client *client, Compositor::instance()->clients())
            wl_client_post_event(client, &selection->selection_offer.object,
                                 WL_SELECTION_OFFER_KEYBOARD_FOCUS, 0);
    }
    wl_display_remove_global(dpy, &selection->selection_offer.object);
    delete selection;
}

void Selection::create(struct wl_client *client, uint32_t id)
{
    delete m_retainedSelection;
    m_retainedSelection = 0;
    m_offerList.clear();
    struct wl_selection *selection = new struct wl_selection;
    memset(selection, 0, sizeof *selection);
    selection->resource.object.id = id;
    selection->resource.object.interface = &wl_selection_interface;
    selection->resource.object.implementation = (void (**)()) &selectionInterface;
    selection->resource.destroy = destroySelection;
    selection->client = client;
    selection->input_device = Compositor::instance()->inputDevice();
    wl_client_add_resource(client, &selection->resource);
}

void Selection::setRetainedSelection(bool enable)
{
    m_retainedSelectionEnabled = enable;
}

void Selection::setRetainedSelectionWatcher(Watcher func, void *param)
{
    m_watchFunc = func;
    m_watchFuncParam = param;
}

void Selection::onClientAdded(wl_client *client)
{
    struct wl_selection *selection = m_currentSelection;
    struct wl_selection_offer *offer = m_currentOffer;
    if (m_retainedSelection) {
        selection = m_retainedSelection;
        offer = &m_retainedSelection->selection_offer;
    }
    if (selection && offer) {
        foreach (const QString &mimeType, m_offerList) {
            QByteArray mimeTypeBa = mimeType.toLatin1();
            wl_client_post_event(client, &offer->object,
                                 WL_SELECTION_OFFER_OFFER, mimeTypeBa.constData());
        }
        wl_client_post_event(client, &offer->object,
                             WL_SELECTION_OFFER_KEYBOARD_FOCUS, selection->input_device);
    }
}

Q_GLOBAL_STATIC(Selection, globalInstance)

Selection *Selection::instance()
{
    return globalInstance();
}

Selection::Selection()
    : m_currentSelection(0), m_currentOffer(0),
      m_retainedReadNotifier(0), m_retainedSelection(0),
      m_retainedSelectionEnabled(false),
      m_watchFunc(0), m_watchFuncParam(0)
{
    connect(Compositor::instance(), SIGNAL(clientAdded(wl_client*)), SLOT(onClientAdded(wl_client*)));
}

Selection::~Selection()
{
    finishReadFromClient();
}

}
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2016 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#include <mapnik/label_collision_detector.hpp>
#include <mapnik/svg/svg_storage.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/marker_helpers.hpp>
#include <mapnik/geometry/geometry_type.hpp>
#include <mapnik/renderer_common/render_markers_symbolizer.hpp>
#include <mapnik/symbolizer.hpp>

namespace mapnik {

namespace detail {

template <typename Detector, typename RendererType, typename ContextType>
struct render_marker_symbolizer_visitor
{
    using vector_dispatch_type = vector_markers_dispatch<Detector>;
    using raster_dispatch_type = raster_markers_dispatch<Detector>;

    render_marker_symbolizer_visitor(std::string const& filename,
                                     markers_symbolizer const& sym,
                                     mapnik::feature_impl & feature,
                                     proj_transform const& prj_trans,
                                     RendererType const& common,
                                     box2d<double> const& clip_box,
                                     ContextType & renderer_context)
        : filename_(filename),
          sym_(sym),
          feature_(feature),
          prj_trans_(prj_trans),
          common_(common),
          clip_box_(clip_box),
          renderer_context_(renderer_context) {}

    svg_attribute_type const& get_marker_attributes(svg_path_ptr const& stock_marker,
                                                    svg_attribute_type & custom_attr) const
    {
        auto const& stock_attr = stock_marker->attributes();
        if (push_explicit_style(stock_attr, custom_attr, sym_, feature_, common_.vars_))
            return custom_attr;
        else
            return stock_attr;
    }

    template <typename Marker, typename Dispatch>
    void render_marker(Marker const& mark, Dispatch & rasterizer_dispatch) const
    {
        auto const& vars = common_.vars_;

        agg::trans_affine geom_tr;
        if (auto geometry_transform = get_optional<transform_type>(sym_, keys::geometry_transform))
        {
            evaluate_transform(geom_tr, feature_, vars, *geometry_transform, common_.scale_factor_);
        }

        vertex_converter_type converter(clip_box_,
                                        sym_,
                                        common_.t_,
                                        prj_trans_,
                                        geom_tr,
                                        feature_,
                                        vars,
                                        common_.scale_factor_);

        bool clip = get<value_bool, keys::clip>(sym_, feature_, vars);
        double offset = get<value_double, keys::offset>(sym_, feature_, vars);
        double simplify_tolerance = get<value_double, keys::simplify_tolerance>(sym_, feature_, vars);
        double smooth = get<value_double, keys::smooth>(sym_, feature_, vars);

        if (clip)
        {
            geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry());
            switch (type)
            {
                case geometry::geometry_types::Polygon:
                case geometry::geometry_types::MultiPolygon:
                    converter.template set<clip_poly_tag>();
                    break;
                case geometry::geometry_types::LineString:
                case geometry::geometry_types::MultiLineString:
                    converter.template set<clip_line_tag>();
                    break;
                default:
                    // silence warning: 4 enumeration values not handled in switch
                    break;
            }
        }

        converter.template set<transform_tag>(); //always transform
        if (std::fabs(offset) > 0.0) converter.template set<offset_transform_tag>(); // parallel offset
        converter.template set<affine_transform_tag>(); // optional affine transform
        if (simplify_tolerance > 0.0) converter.template set<simplify_tag>(); // optional simplify converter
        if (smooth > 0.0) converter.template set<smooth_tag>(); // optional smooth converter

        apply_markers_multi(feature_, vars, converter, rasterizer_dispatch, sym_);
    }

    void operator() (marker_null const&) const {}

    void operator() (marker_svg const& mark) const
    {
        using namespace mapnik::svg;

        // https://github.com/mapnik/mapnik/issues/1316
        bool snap_to_pixels = !mapnik::marker_cache::instance().is_uri(filename_);

        agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);

        boost::optional<svg_path_ptr> const& stock_vector_marker = mark.get_data();
        svg_path_ptr marker_ptr = *stock_vector_marker;
        bool is_ellipse = false;

        svg_attribute_type s_attributes;
        auto const& r_attributes = get_marker_attributes(*stock_vector_marker, s_attributes);

        // special case for simple ellipse markers
        // to allow for full control over rx/ry dimensions
        if (filename_ == "shape://ellipse"
           && (has_key(sym_,keys::width) || has_key(sym_,keys::height)))
        {
            marker_ptr = std::make_shared<svg_storage_type>();
            is_ellipse = true;
        }
        else
        {
            box2d<double> const& bbox = mark.bounding_box();
            setup_transform_scaling(image_tr, bbox.width(), bbox.height(), feature_, common_.vars_, sym_);
        }

        vertex_stl_adapter<svg_path_storage> stl_storage(marker_ptr->source());
        svg_path_adapter svg_path(stl_storage);

        if (is_ellipse)
        {
            build_ellipse(sym_, feature_, common_.vars_, *marker_ptr, svg_path);
        }

        if (auto image_transform = get_optional<transform_type>(sym_, keys::image_transform))
        {
            evaluate_transform(image_tr, feature_, common_.vars_, *image_transform, common_.scale_factor_);
        }

        vector_dispatch_type rasterizer_dispatch(marker_ptr,
                                                 svg_path,
                                                 r_attributes,
                                                 image_tr,
                                                 sym_,
                                                 *common_.detector_,
                                                 common_.scale_factor_,
                                                 feature_,
                                                 common_.vars_,
                                                 snap_to_pixels,
                                                 renderer_context_);

        render_marker(mark, rasterizer_dispatch);
    }

    void operator() (marker_rgba8 const& mark) const
    {
        agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);

        setup_transform_scaling(image_tr, mark.width(), mark.height(), feature_, common_.vars_, sym_);
        auto image_transform = get_optional<transform_type>(sym_, keys::image_transform);
        if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform, common_.scale_factor_);
        box2d<double> const& bbox = mark.bounding_box();
        mapnik::image_rgba8 const& marker = mark.get_data();
        // - clamp sizes to > 4 pixels of interactivity
        coord2d center = bbox.center();
        agg::trans_affine_translation recenter(-center.x, -center.y);
        agg::trans_affine marker_trans = recenter * image_tr;
        raster_dispatch_type rasterizer_dispatch(marker,
                                                 marker_trans,
                                                 sym_,
                                                 *common_.detector_,
                                                 common_.scale_factor_,
                                                 feature_,
                                                 common_.vars_,
                                                 renderer_context_);

        render_marker(mark, rasterizer_dispatch);
    }

  private:
    std::string const& filename_;
    markers_symbolizer const& sym_;
    mapnik::feature_impl & feature_;
    proj_transform const& prj_trans_;
    RendererType const& common_;
    box2d<double> const& clip_box_;
    ContextType & renderer_context_;
};

} // namespace detail

markers_dispatch_params::markers_dispatch_params(box2d<double> const& size,
                                                 agg::trans_affine const& tr,
                                                 symbolizer_base const& sym,
                                                 feature_impl const& feature,
                                                 attributes const& vars,
                                                 double scale,
                                                 bool snap)
    : placement_params{
        size,
        tr,
        get<value_double, keys::spacing>(sym, feature, vars),
        get<value_double, keys::max_error>(sym, feature, vars),
        get<value_bool, keys::allow_overlap>(sym, feature, vars),
        get<value_bool, keys::avoid_edges>(sym, feature, vars),
        get<direction_enum, keys::direction>(sym, feature, vars)}
    , placement_method(get<marker_placement_enum, keys::markers_placement_type>(sym, feature, vars))
    , ignore_placement(get<value_bool, keys::ignore_placement>(sym, feature, vars))
    , snap_to_pixels(snap)
    , scale_factor(scale)
    , opacity(get<value_double, keys::opacity>(sym, feature, vars))
{
    placement_params.spacing *= scale;
}

void render_markers_symbolizer(markers_symbolizer const& sym,
                               mapnik::feature_impl & feature,
                               proj_transform const& prj_trans,
                               renderer_common const& common,
                               box2d<double> const& clip_box,
                               markers_renderer_context & renderer_context)
{
    using Detector = label_collision_detector4;
    using RendererType = renderer_common;
    using ContextType = markers_renderer_context;
    using VisitorType = detail::render_marker_symbolizer_visitor<Detector,
                                                                 RendererType,
                                                                 ContextType>;

    std::string filename = get<std::string>(sym, keys::file, feature, common.vars_, "shape://ellipse");
    if (!filename.empty())
    {
        auto mark = mapnik::marker_cache::instance().find(filename, true);
        VisitorType visitor(filename, sym, feature, prj_trans, common, clip_box,
                            renderer_context);
        util::apply_visitor(visitor, *mark);
    }
}

} // namespace mapnik
<commit_msg>strip boost::optional from non-optional marker ptr<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2016 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#include <mapnik/label_collision_detector.hpp>
#include <mapnik/svg/svg_storage.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/marker_helpers.hpp>
#include <mapnik/geometry/geometry_type.hpp>
#include <mapnik/renderer_common/render_markers_symbolizer.hpp>
#include <mapnik/symbolizer.hpp>

namespace mapnik {

namespace detail {

template <typename Detector, typename RendererType, typename ContextType>
struct render_marker_symbolizer_visitor
{
    using vector_dispatch_type = vector_markers_dispatch<Detector>;
    using raster_dispatch_type = raster_markers_dispatch<Detector>;

    render_marker_symbolizer_visitor(std::string const& filename,
                                     markers_symbolizer const& sym,
                                     mapnik::feature_impl & feature,
                                     proj_transform const& prj_trans,
                                     RendererType const& common,
                                     box2d<double> const& clip_box,
                                     ContextType & renderer_context)
        : filename_(filename),
          sym_(sym),
          feature_(feature),
          prj_trans_(prj_trans),
          common_(common),
          clip_box_(clip_box),
          renderer_context_(renderer_context) {}

    svg_attribute_type const& get_marker_attributes(svg_path_ptr const& stock_marker,
                                                    svg_attribute_type & custom_attr) const
    {
        auto const& stock_attr = stock_marker->attributes();
        if (push_explicit_style(stock_attr, custom_attr, sym_, feature_, common_.vars_))
            return custom_attr;
        else
            return stock_attr;
    }

    template <typename Marker, typename Dispatch>
    void render_marker(Marker const& mark, Dispatch & rasterizer_dispatch) const
    {
        auto const& vars = common_.vars_;

        agg::trans_affine geom_tr;
        if (auto geometry_transform = get_optional<transform_type>(sym_, keys::geometry_transform))
        {
            evaluate_transform(geom_tr, feature_, vars, *geometry_transform, common_.scale_factor_);
        }

        vertex_converter_type converter(clip_box_,
                                        sym_,
                                        common_.t_,
                                        prj_trans_,
                                        geom_tr,
                                        feature_,
                                        vars,
                                        common_.scale_factor_);

        bool clip = get<value_bool, keys::clip>(sym_, feature_, vars);
        double offset = get<value_double, keys::offset>(sym_, feature_, vars);
        double simplify_tolerance = get<value_double, keys::simplify_tolerance>(sym_, feature_, vars);
        double smooth = get<value_double, keys::smooth>(sym_, feature_, vars);

        if (clip)
        {
            geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry());
            switch (type)
            {
                case geometry::geometry_types::Polygon:
                case geometry::geometry_types::MultiPolygon:
                    converter.template set<clip_poly_tag>();
                    break;
                case geometry::geometry_types::LineString:
                case geometry::geometry_types::MultiLineString:
                    converter.template set<clip_line_tag>();
                    break;
                default:
                    // silence warning: 4 enumeration values not handled in switch
                    break;
            }
        }

        converter.template set<transform_tag>(); //always transform
        if (std::fabs(offset) > 0.0) converter.template set<offset_transform_tag>(); // parallel offset
        converter.template set<affine_transform_tag>(); // optional affine transform
        if (simplify_tolerance > 0.0) converter.template set<simplify_tag>(); // optional simplify converter
        if (smooth > 0.0) converter.template set<smooth_tag>(); // optional smooth converter

        apply_markers_multi(feature_, vars, converter, rasterizer_dispatch, sym_);
    }

    void operator() (marker_null const&) const {}

    void operator() (marker_svg const& mark) const
    {
        using namespace mapnik::svg;

        // https://github.com/mapnik/mapnik/issues/1316
        bool snap_to_pixels = !mapnik::marker_cache::instance().is_uri(filename_);

        agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);

        svg_path_ptr stock_vector_marker = mark.get_data();
        svg_path_ptr marker_ptr = stock_vector_marker;
        bool is_ellipse = false;

        svg_attribute_type s_attributes;
        auto const& r_attributes = get_marker_attributes(stock_vector_marker, s_attributes);

        // special case for simple ellipse markers
        // to allow for full control over rx/ry dimensions
        if (filename_ == "shape://ellipse"
           && (has_key(sym_,keys::width) || has_key(sym_,keys::height)))
        {
            marker_ptr = std::make_shared<svg_storage_type>();
            is_ellipse = true;
        }
        else
        {
            box2d<double> const& bbox = mark.bounding_box();
            setup_transform_scaling(image_tr, bbox.width(), bbox.height(), feature_, common_.vars_, sym_);
        }

        vertex_stl_adapter<svg_path_storage> stl_storage(marker_ptr->source());
        svg_path_adapter svg_path(stl_storage);

        if (is_ellipse)
        {
            build_ellipse(sym_, feature_, common_.vars_, *marker_ptr, svg_path);
        }

        if (auto image_transform = get_optional<transform_type>(sym_, keys::image_transform))
        {
            evaluate_transform(image_tr, feature_, common_.vars_, *image_transform, common_.scale_factor_);
        }

        vector_dispatch_type rasterizer_dispatch(marker_ptr,
                                                 svg_path,
                                                 r_attributes,
                                                 image_tr,
                                                 sym_,
                                                 *common_.detector_,
                                                 common_.scale_factor_,
                                                 feature_,
                                                 common_.vars_,
                                                 snap_to_pixels,
                                                 renderer_context_);

        render_marker(mark, rasterizer_dispatch);
    }

    void operator() (marker_rgba8 const& mark) const
    {
        agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);

        setup_transform_scaling(image_tr, mark.width(), mark.height(), feature_, common_.vars_, sym_);
        auto image_transform = get_optional<transform_type>(sym_, keys::image_transform);
        if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform, common_.scale_factor_);
        box2d<double> const& bbox = mark.bounding_box();
        mapnik::image_rgba8 const& marker = mark.get_data();
        // - clamp sizes to > 4 pixels of interactivity
        coord2d center = bbox.center();
        agg::trans_affine_translation recenter(-center.x, -center.y);
        agg::trans_affine marker_trans = recenter * image_tr;
        raster_dispatch_type rasterizer_dispatch(marker,
                                                 marker_trans,
                                                 sym_,
                                                 *common_.detector_,
                                                 common_.scale_factor_,
                                                 feature_,
                                                 common_.vars_,
                                                 renderer_context_);

        render_marker(mark, rasterizer_dispatch);
    }

  private:
    std::string const& filename_;
    markers_symbolizer const& sym_;
    mapnik::feature_impl & feature_;
    proj_transform const& prj_trans_;
    RendererType const& common_;
    box2d<double> const& clip_box_;
    ContextType & renderer_context_;
};

} // namespace detail

markers_dispatch_params::markers_dispatch_params(box2d<double> const& size,
                                                 agg::trans_affine const& tr,
                                                 symbolizer_base const& sym,
                                                 feature_impl const& feature,
                                                 attributes const& vars,
                                                 double scale,
                                                 bool snap)
    : placement_params{
        size,
        tr,
        get<value_double, keys::spacing>(sym, feature, vars),
        get<value_double, keys::max_error>(sym, feature, vars),
        get<value_bool, keys::allow_overlap>(sym, feature, vars),
        get<value_bool, keys::avoid_edges>(sym, feature, vars),
        get<direction_enum, keys::direction>(sym, feature, vars)}
    , placement_method(get<marker_placement_enum, keys::markers_placement_type>(sym, feature, vars))
    , ignore_placement(get<value_bool, keys::ignore_placement>(sym, feature, vars))
    , snap_to_pixels(snap)
    , scale_factor(scale)
    , opacity(get<value_double, keys::opacity>(sym, feature, vars))
{
    placement_params.spacing *= scale;
}

void render_markers_symbolizer(markers_symbolizer const& sym,
                               mapnik::feature_impl & feature,
                               proj_transform const& prj_trans,
                               renderer_common const& common,
                               box2d<double> const& clip_box,
                               markers_renderer_context & renderer_context)
{
    using Detector = label_collision_detector4;
    using RendererType = renderer_common;
    using ContextType = markers_renderer_context;
    using VisitorType = detail::render_marker_symbolizer_visitor<Detector,
                                                                 RendererType,
                                                                 ContextType>;

    std::string filename = get<std::string>(sym, keys::file, feature, common.vars_, "shape://ellipse");
    if (!filename.empty())
    {
        auto mark = mapnik::marker_cache::instance().find(filename, true);
        VisitorType visitor(filename, sym, feature, prj_trans, common, clip_box,
                            renderer_context);
        util::apply_visitor(visitor, *mark);
    }
}

} // namespace mapnik
<|endoftext|>


<commit_before>// Copyright (c) 2011 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/browser/plugin_data_remover_impl.h"

#include "base/bind.h"
#include "base/metrics/histogram.h"
#include "base/synchronization/waitable_event.h"
#include "base/version.h"
#include "content/browser/plugin_process_host.h"
#include "content/browser/plugin_service.h"
#include "content/common/child_process_host_impl.h"
#include "content/common/plugin_messages.h"
#include "content/public/browser/browser_thread.h"
#include "webkit/plugins/npapi/plugin_group.h"

using content::BrowserThread;
using content::ChildProcessHostImpl;

namespace {

const char kFlashMimeType[] = "application/x-shockwave-flash";
// The minimum Flash Player version that implements NPP_ClearSiteData.
const char kMinFlashVersion[] = "10.3";
const int64 kRemovalTimeoutMs = 10000;
const uint64 kClearAllData = 0;

}  // namespace

namespace content {

// static
PluginDataRemover* PluginDataRemover::Create(
    const content::ResourceContext& resource_context) {
  return new PluginDataRemoverImpl(resource_context);
}

// static
bool PluginDataRemover::IsSupported(webkit::WebPluginInfo* plugin) {
  bool allow_wildcard = false;
  std::vector<webkit::WebPluginInfo> plugins;
  PluginService::GetInstance()->GetPluginInfoArray(
      GURL(), kFlashMimeType, allow_wildcard, &plugins, NULL);
  std::vector<webkit::WebPluginInfo>::iterator plugin_it = plugins.begin();
  if (plugin_it == plugins.end())
    return false;
  scoped_ptr<Version> version(
      webkit::npapi::PluginGroup::CreateVersionFromString(plugin_it->version));
  scoped_ptr<Version> min_version(
      Version::GetVersionFromString(kMinFlashVersion));
  bool rv = version.get() && min_version->CompareTo(*version) == -1;
  if (rv)
    *plugin = *plugin_it;
  return rv;
}

}

class PluginDataRemoverImpl::Context
    : public PluginProcessHost::Client,
      public IPC::Channel::Listener,
      public base::RefCountedThreadSafe<Context> {
 public:
  Context(const std::string& mime_type,
          base::Time begin_time,
          const content::ResourceContext& resource_context)
      : event_(new base::WaitableEvent(true, false)),
        begin_time_(begin_time),
        is_removing_(false),
        resource_context_(resource_context),
        channel_(NULL) {
    // Balanced in OnChannelOpened or OnError. Exactly one them will eventually
    // be called, so we need to keep this object around until then.
    AddRef();
    remove_start_time_ = base::Time::Now();
    BrowserThread::PostTask(
        BrowserThread::IO,
        FROM_HERE,
        base::Bind(&Context::Init, this, mime_type));

    BrowserThread::PostDelayedTask(
        BrowserThread::IO,
        FROM_HERE,
        base::Bind(&Context::OnTimeout, this),
        kRemovalTimeoutMs);
  }

  virtual ~Context() {
    if (channel_)
      BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, channel_);
  }

  // PluginProcessHost::Client methods.
  virtual int ID() OVERRIDE {
    // Generate a unique identifier for this PluginProcessHostClient.
    return ChildProcessHostImpl::GenerateChildProcessUniqueId();
  }

  virtual bool OffTheRecord() OVERRIDE {
    return false;
  }

  virtual const content::ResourceContext& GetResourceContext() OVERRIDE {
    return resource_context_;
  }

  virtual void SetPluginInfo(const webkit::WebPluginInfo& info) OVERRIDE {
  }

  virtual void OnFoundPluginProcessHost(PluginProcessHost* host) OVERRIDE {
  }

  virtual void OnSentPluginChannelRequest() OVERRIDE {
  }

  virtual void OnChannelOpened(const IPC::ChannelHandle& handle) OVERRIDE {
    ConnectToChannel(handle);
    // Balancing the AddRef call.
    Release();
  }

  virtual void OnError() OVERRIDE {
    LOG(DFATAL) << "Couldn't open plugin channel";
    SignalDone();
    // Balancing the AddRef call.
    Release();
  }

  // IPC::Channel::Listener methods.
  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
    IPC_BEGIN_MESSAGE_MAP(Context, message)
      IPC_MESSAGE_HANDLER(PluginHostMsg_ClearSiteDataResult,
                          OnClearSiteDataResult)
      IPC_MESSAGE_UNHANDLED_ERROR()
    IPC_END_MESSAGE_MAP()

    return true;
  }

  virtual void OnChannelError() OVERRIDE {
    if (is_removing_) {
      NOTREACHED() << "Channel error";
      SignalDone();
    }
  }

  base::WaitableEvent* event() { return event_.get(); }

 private:
  // Initialize on the IO thread.
  void Init(const std::string& mime_type) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    is_removing_ = true;
    PluginService::GetInstance()->OpenChannelToNpapiPlugin(
        0, 0, GURL(), GURL(), mime_type, this);
  }

  // Connects the client side of a newly opened plug-in channel.
  void ConnectToChannel(const IPC::ChannelHandle& handle) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));

    // If we timed out, don't bother connecting.
    if (!is_removing_)
      return;

    DCHECK(!channel_);
    channel_ = new IPC::Channel(handle, IPC::Channel::MODE_CLIENT, this);
    if (!channel_->Connect()) {
      NOTREACHED() << "Couldn't connect to plugin";
      SignalDone();
      return;
    }

    if (!channel_->Send(new PluginMsg_ClearSiteData(std::string(),
                                                    kClearAllData,
                                                    begin_time_))) {
      NOTREACHED() << "Couldn't send ClearSiteData message";
      SignalDone();
      return;
    }
  }

  // Handles the PluginHostMsg_ClearSiteDataResult message.
  void OnClearSiteDataResult(bool success) {
    LOG_IF(ERROR, !success) << "ClearSiteData returned error";
    UMA_HISTOGRAM_TIMES("ClearPluginData.time",
                        base::Time::Now() - remove_start_time_);
    SignalDone();
  }

  // Called when a timeout happens in order not to block the client
  // indefinitely.
  void OnTimeout() {
    LOG_IF(ERROR, is_removing_) << "Timed out";
    SignalDone();
  }

  // Signals that we are finished with removing data (successful or not). This
  // method is safe to call multiple times.
  void SignalDone() {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    if (!is_removing_)
      return;
    is_removing_ = false;
    event_->Signal();
  }

  scoped_ptr<base::WaitableEvent> event_;
  // The point in time when we start removing data.
  base::Time remove_start_time_;
  // The point in time from which on we remove data.
  base::Time begin_time_;
  bool is_removing_;

  // The resource context for the profile.
  const content::ResourceContext& resource_context_;

  // We own the channel, but it's used on the IO thread, so it needs to be
  // deleted there. It's NULL until we have opened a connection to the plug-in
  // process.
  IPC::Channel* channel_;
};

PluginDataRemoverImpl::PluginDataRemoverImpl(
    const content::ResourceContext& resource_context)
    : mime_type_(kFlashMimeType),
      resource_context_(resource_context) {
}

PluginDataRemoverImpl::~PluginDataRemoverImpl() {
}

base::WaitableEvent* PluginDataRemoverImpl::StartRemoving(
    base::Time begin_time) {
  DCHECK(!context_.get());
  context_ = new Context(mime_type_, begin_time, resource_context_);
  return context_->event();
}
<commit_msg>Make sure PluginDataRemoverImpl::Init is executed after the constructor has finished.<commit_after>// Copyright (c) 2011 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/browser/plugin_data_remover_impl.h"

#include "base/bind.h"
#include "base/metrics/histogram.h"
#include "base/synchronization/waitable_event.h"
#include "base/version.h"
#include "content/browser/plugin_process_host.h"
#include "content/browser/plugin_service.h"
#include "content/common/child_process_host_impl.h"
#include "content/common/plugin_messages.h"
#include "content/public/browser/browser_thread.h"
#include "webkit/plugins/npapi/plugin_group.h"

using content::BrowserThread;
using content::ChildProcessHostImpl;

namespace {

const char kFlashMimeType[] = "application/x-shockwave-flash";
// The minimum Flash Player version that implements NPP_ClearSiteData.
const char kMinFlashVersion[] = "10.3";
const int64 kRemovalTimeoutMs = 10000;
const uint64 kClearAllData = 0;

}  // namespace

namespace content {

// static
PluginDataRemover* PluginDataRemover::Create(
    const content::ResourceContext& resource_context) {
  return new PluginDataRemoverImpl(resource_context);
}

// static
bool PluginDataRemover::IsSupported(webkit::WebPluginInfo* plugin) {
  bool allow_wildcard = false;
  std::vector<webkit::WebPluginInfo> plugins;
  PluginService::GetInstance()->GetPluginInfoArray(
      GURL(), kFlashMimeType, allow_wildcard, &plugins, NULL);
  std::vector<webkit::WebPluginInfo>::iterator plugin_it = plugins.begin();
  if (plugin_it == plugins.end())
    return false;
  scoped_ptr<Version> version(
      webkit::npapi::PluginGroup::CreateVersionFromString(plugin_it->version));
  scoped_ptr<Version> min_version(
      Version::GetVersionFromString(kMinFlashVersion));
  bool rv = version.get() && min_version->CompareTo(*version) == -1;
  if (rv)
    *plugin = *plugin_it;
  return rv;
}

}

class PluginDataRemoverImpl::Context
    : public PluginProcessHost::Client,
      public IPC::Channel::Listener,
      public base::RefCountedThreadSafe<Context,
                                        BrowserThread::DeleteOnIOThread> {
 public:
  Context(base::Time begin_time,
          const content::ResourceContext& resource_context)
      : event_(new base::WaitableEvent(true, false)),
        begin_time_(begin_time),
        is_removing_(false),
        resource_context_(resource_context),
        channel_(NULL) {
  }

  virtual ~Context() {
  }

  void Init(const std::string& mime_type) {
    BrowserThread::PostTask(
        BrowserThread::IO,
        FROM_HERE,
        base::Bind(&Context::InitOnIOThread, this, mime_type));
    BrowserThread::PostDelayedTask(
        BrowserThread::IO,
        FROM_HERE,
        base::Bind(&Context::OnTimeout, this),
        kRemovalTimeoutMs);
  }

  // Initialize on the IO thread.
  void InitOnIOThread(const std::string& mime_type) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    remove_start_time_ = base::Time::Now();
    is_removing_ = true;
    // Balanced in OnChannelOpened or OnError. Exactly one them will eventually
    // be called, so we need to keep this object around until then.
    AddRef();
    PluginService::GetInstance()->OpenChannelToNpapiPlugin(
        0, 0, GURL(), GURL(), mime_type, this);
  }

  // Called when a timeout happens in order not to block the client
  // indefinitely.
  void OnTimeout() {
    LOG_IF(ERROR, is_removing_) << "Timed out";
    SignalDone();
  }

  // PluginProcessHost::Client methods.
  virtual int ID() OVERRIDE {
    // Generate a unique identifier for this PluginProcessHostClient.
    return ChildProcessHostImpl::GenerateChildProcessUniqueId();
  }

  virtual bool OffTheRecord() OVERRIDE {
    return false;
  }

  virtual const content::ResourceContext& GetResourceContext() OVERRIDE {
    return resource_context_;
  }

  virtual void SetPluginInfo(const webkit::WebPluginInfo& info) OVERRIDE {
  }

  virtual void OnFoundPluginProcessHost(PluginProcessHost* host) OVERRIDE {
  }

  virtual void OnSentPluginChannelRequest() OVERRIDE {
  }

  virtual void OnChannelOpened(const IPC::ChannelHandle& handle) OVERRIDE {
    ConnectToChannel(handle);
    // Balancing the AddRef call.
    Release();
  }

  virtual void OnError() OVERRIDE {
    LOG(DFATAL) << "Couldn't open plugin channel";
    SignalDone();
    // Balancing the AddRef call.
    Release();
  }

  // IPC::Channel::Listener methods.
  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
    IPC_BEGIN_MESSAGE_MAP(Context, message)
      IPC_MESSAGE_HANDLER(PluginHostMsg_ClearSiteDataResult,
                          OnClearSiteDataResult)
      IPC_MESSAGE_UNHANDLED_ERROR()
    IPC_END_MESSAGE_MAP()

    return true;
  }

  virtual void OnChannelError() OVERRIDE {
    if (is_removing_) {
      NOTREACHED() << "Channel error";
      SignalDone();
    }
  }

  base::WaitableEvent* event() { return event_.get(); }

 private:
  // Connects the client side of a newly opened plug-in channel.
  void ConnectToChannel(const IPC::ChannelHandle& handle) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));

    // If we timed out, don't bother connecting.
    if (!is_removing_)
      return;

    DCHECK(!channel_.get());
    channel_.reset(new IPC::Channel(handle, IPC::Channel::MODE_CLIENT, this));
    if (!channel_->Connect()) {
      NOTREACHED() << "Couldn't connect to plugin";
      SignalDone();
      return;
    }

    if (!channel_->Send(new PluginMsg_ClearSiteData(std::string(),
                                                    kClearAllData,
                                                    begin_time_))) {
      NOTREACHED() << "Couldn't send ClearSiteData message";
      SignalDone();
      return;
    }
  }

  // Handles the PluginHostMsg_ClearSiteDataResult message.
  void OnClearSiteDataResult(bool success) {
    LOG_IF(ERROR, !success) << "ClearSiteData returned error";
    UMA_HISTOGRAM_TIMES("ClearPluginData.time",
                        base::Time::Now() - remove_start_time_);
    SignalDone();
  }

  // Signals that we are finished with removing data (successful or not). This
  // method is safe to call multiple times.
  void SignalDone() {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    if (!is_removing_)
      return;
    is_removing_ = false;
    event_->Signal();
  }

  scoped_ptr<base::WaitableEvent> event_;
  // The point in time when we start removing data.
  base::Time remove_start_time_;
  // The point in time from which on we remove data.
  base::Time begin_time_;
  bool is_removing_;

  // The resource context for the profile.
  const content::ResourceContext& resource_context_;

  // The channel is NULL until we have opened a connection to the plug-in
  // process.
  scoped_ptr<IPC::Channel> channel_;
};

PluginDataRemoverImpl::PluginDataRemoverImpl(
    const content::ResourceContext& resource_context)
    : mime_type_(kFlashMimeType),
      resource_context_(resource_context) {
}

PluginDataRemoverImpl::~PluginDataRemoverImpl() {
}

base::WaitableEvent* PluginDataRemoverImpl::StartRemoving(
    base::Time begin_time) {
  DCHECK(!context_.get());
  context_ = new Context(begin_time, resource_context_);
  context_->Init(mime_type_);
  return context_->event();
}
<|endoftext|>


<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 "content/browser/speech/speech_recognizer.h"

#include "base/time.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/net/url_request_context_getter.h"
#include "content/browser/browser_thread.h"

using media::AudioInputController;
using std::string;

namespace {

// The following constants are related to the volume level indicator shown in
// the UI for recorded audio.
// Multiplier used when new volume is greater than previous level.
const float kUpSmoothingFactor = 1.0f;
// Multiplier used when new volume is lesser than previous level.
const float kDownSmoothingFactor = 0.7f;
// RMS dB value of a maximum (unclipped) sine wave for int16 samples.
const float kAudioMeterMaxDb = 90.31f;
// This value corresponds to RMS dB for int16 with 6 most-significant-bits = 0.
// Values lower than this will display as empty level-meter.
const float kAudioMeterMinDb = 30.0f;
const float kAudioMeterDbRange = kAudioMeterMaxDb - kAudioMeterMinDb;

// Maximum level to draw to display unclipped meter. (1.0f displays clipping.)
const float kAudioMeterRangeMaxUnclipped = 47.0f / 48.0f;

// Returns true if more than 5% of the samples are at min or max value.
bool Clipping(const int16* samples, int num_samples) {
  int clipping_samples = 0;
  const int kThreshold = num_samples / 20;
  for (int i = 0; i < num_samples; ++i) {
    if (samples[i] <= -32767 || samples[i] >= 32767) {
      if (++clipping_samples > kThreshold)
        return true;
    }
  }
  return false;
}

}  // namespace

namespace speech_input {

const int SpeechRecognizer::kAudioSampleRate = 16000;
const int SpeechRecognizer::kAudioPacketIntervalMs = 100;
const int SpeechRecognizer::kNumAudioChannels = 1;
const int SpeechRecognizer::kNumBitsPerAudioSample = 16;
const int SpeechRecognizer::kNoSpeechTimeoutSec = 8;
const int SpeechRecognizer::kEndpointerEstimationTimeMs = 300;

SpeechRecognizer::SpeechRecognizer(Delegate* delegate,
                                   int caller_id,
                                   const std::string& language,
                                   const std::string& grammar,
                                   const std::string& hardware_info,
                                   const std::string& origin_url)
    : delegate_(delegate),
      caller_id_(caller_id),
      language_(language),
      grammar_(grammar),
      hardware_info_(hardware_info),
      origin_url_(origin_url),
      codec_(AudioEncoder::CODEC_SPEEX),
      encoder_(NULL),
      endpointer_(kAudioSampleRate),
      num_samples_recorded_(0),
      audio_level_(0.0f) {
  endpointer_.set_speech_input_complete_silence_length(
      base::Time::kMicrosecondsPerSecond / 2);
  endpointer_.set_long_speech_input_complete_silence_length(
      base::Time::kMicrosecondsPerSecond);
  endpointer_.set_long_speech_length(3 * base::Time::kMicrosecondsPerSecond);
  endpointer_.StartSession();
}

SpeechRecognizer::~SpeechRecognizer() {
  // Recording should have stopped earlier due to the endpointer or
  // |StopRecording| being called.
  DCHECK(!audio_controller_.get());
  DCHECK(!request_.get() || !request_->HasPendingRequest());
  DCHECK(!encoder_.get());
  endpointer_.EndSession();
}

bool SpeechRecognizer::StartRecording() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  DCHECK(!audio_controller_.get());
  DCHECK(!request_.get() || !request_->HasPendingRequest());
  DCHECK(!encoder_.get());

  // The endpointer needs to estimate the environment/background noise before
  // starting to treat the audio as user input. In |HandleOnData| we wait until
  // such time has passed before switching to user input mode.
  endpointer_.SetEnvironmentEstimationMode();

  encoder_.reset(AudioEncoder::Create(codec_, kAudioSampleRate,
                                      kNumBitsPerAudioSample));
  int samples_per_packet = (kAudioSampleRate * kAudioPacketIntervalMs) / 1000;
  AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, kNumAudioChannels,
                         kAudioSampleRate, kNumBitsPerAudioSample,
                         samples_per_packet);
  audio_controller_ = AudioInputController::Create(this, params);
  DCHECK(audio_controller_.get());
  VLOG(1) << "SpeechRecognizer starting record.";
  num_samples_recorded_ = 0;
  audio_controller_->Record();

  return true;
}

void SpeechRecognizer::CancelRecognition() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  DCHECK(audio_controller_.get() || request_.get());

  // Stop recording if required.
  if (audio_controller_.get()) {
    VLOG(1) << "SpeechRecognizer stopping record.";
    audio_controller_->Close();
    audio_controller_ = NULL;  // Releases the ref ptr.
  }

  VLOG(1) << "SpeechRecognizer canceling recognition.";
  encoder_.reset();
  request_.reset();
}

void SpeechRecognizer::StopRecording() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));

  // If audio recording has already stopped and we are in recognition phase,
  // silently ignore any more calls to stop recording.
  if (!audio_controller_.get())
    return;

  VLOG(1) << "SpeechRecognizer stopping record.";
  audio_controller_->Close();
  audio_controller_ = NULL;  // Releases the ref ptr.

  delegate_->DidCompleteRecording(caller_id_);

  // UploadAudioChunk requires a non-empty final buffer. So we encode a packet
  // of silence in case encoder had no data already.
  std::vector<short> samples((kAudioSampleRate * kAudioPacketIntervalMs) /
                             1000);
  encoder_->Encode(&samples[0], samples.size());
  encoder_->Flush();
  string encoded_data;
  encoder_->GetEncodedDataAndClear(&encoded_data);
  DCHECK(!encoded_data.empty());
  encoder_.reset();

  // If we haven't got any audio yet end the recognition sequence here.
  if (request_ == NULL) {
    // Guard against the delegate freeing us until we finish our job.
    scoped_refptr<SpeechRecognizer> me(this);
    delegate_->DidCompleteRecognition(caller_id_);
  } else {
    request_->UploadAudioChunk(encoded_data, true /* is_last_chunk */);
  }
}

// Invoked in the audio thread.
void SpeechRecognizer::OnError(AudioInputController* controller,
                               int error_code) {
  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
                         NewRunnableMethod(this,
                                           &SpeechRecognizer::HandleOnError,
                                           error_code));
}

void SpeechRecognizer::HandleOnError(int error_code) {
  LOG(WARNING) << "SpeechRecognizer::HandleOnError, code=" << error_code;

  // Check if we are still recording before canceling recognition, as
  // recording might have been stopped after this error was posted to the queue
  // by |OnError|.
  if (!audio_controller_.get())
    return;

  InformErrorAndCancelRecognition(RECOGNIZER_ERROR_CAPTURE);
}

void SpeechRecognizer::OnData(AudioInputController* controller,
                              const uint8* data, uint32 size) {
  if (size == 0)  // This could happen when recording stops and is normal.
    return;

  string* str_data = new string(reinterpret_cast<const char*>(data), size);
  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
                         NewRunnableMethod(this,
                                           &SpeechRecognizer::HandleOnData,
                                           str_data));
}

void SpeechRecognizer::HandleOnData(string* data) {
  // Check if we are still recording and if not discard this buffer, as
  // recording might have been stopped after this buffer was posted to the queue
  // by |OnData|.
  if (!audio_controller_.get()) {
    delete data;
    return;
  }

  const short* samples = reinterpret_cast<const short*>(data->data());
  DCHECK((data->length() % sizeof(short)) == 0);
  int num_samples = data->length() / sizeof(short);
  encoder_->Encode(samples, num_samples);
  float rms;
  endpointer_.ProcessAudio(samples, num_samples, &rms);
  bool did_clip = Clipping(samples, num_samples);
  delete data;
  num_samples_recorded_ += num_samples;

  if (request_ == NULL) {
    // This was the first audio packet recorded, so start a request to the
    // server to send the data.
    request_.reset(new SpeechRecognitionRequest(
        Profile::GetDefaultRequestContext(), this));
    request_->Start(language_, grammar_, hardware_info_, origin_url_,
                    encoder_->mime_type());
  }

  string encoded_data;
  encoder_->GetEncodedDataAndClear(&encoded_data);
  DCHECK(!encoded_data.empty());
  request_->UploadAudioChunk(encoded_data, false /* is_last_chunk */);

  if (endpointer_.IsEstimatingEnvironment()) {
    // Check if we have gathered enough audio for the endpointer to do
    // environment estimation and should move on to detect speech/end of speech.
    if (num_samples_recorded_ >= (kEndpointerEstimationTimeMs *
                                  kAudioSampleRate) / 1000) {
      endpointer_.SetUserInputMode();
      delegate_->DidCompleteEnvironmentEstimation(caller_id_);
    }
    return;  // No more processing since we are still estimating environment.
  }

  // Check if we have waited too long without hearing any speech.
  if (!endpointer_.DidStartReceivingSpeech() &&
      num_samples_recorded_ >= kNoSpeechTimeoutSec * kAudioSampleRate) {
    InformErrorAndCancelRecognition(RECOGNIZER_ERROR_NO_SPEECH);
    return;
  }

  // Calculate the input volume to display in the UI, smoothing towards the
  // new level.
  float level = (rms - kAudioMeterMinDb) /
      (kAudioMeterDbRange / kAudioMeterRangeMaxUnclipped);
  level = std::min(std::max(0.0f, level), kAudioMeterRangeMaxUnclipped);
  if (level > audio_level_) {
    audio_level_ += (level - audio_level_) * kUpSmoothingFactor;
  } else {
    audio_level_ += (level - audio_level_) * kDownSmoothingFactor;
  }

  float noise_level = (endpointer_.NoiseLevelDb() - kAudioMeterMinDb) /
      (kAudioMeterDbRange / kAudioMeterRangeMaxUnclipped);
  noise_level = std::min(std::max(0.0f, noise_level),
      kAudioMeterRangeMaxUnclipped);

  delegate_->SetInputVolume(caller_id_, did_clip ? 1.0f : audio_level_,
                            noise_level);

  if (endpointer_.speech_input_complete()) {
    StopRecording();
  }

  // TODO(satish): Once we have streaming POST, start sending the data received
  // here as POST chunks.
}

void SpeechRecognizer::SetRecognitionResult(
    bool error, const SpeechInputResultArray& result) {
  if (error || result.empty()) {
    InformErrorAndCancelRecognition(error ? RECOGNIZER_ERROR_NETWORK :
                                            RECOGNIZER_ERROR_NO_RESULTS);
    return;
  }

  delegate_->SetRecognitionResult(caller_id_, error, result);

  // Guard against the delegate freeing us until we finish our job.
  scoped_refptr<SpeechRecognizer> me(this);
  delegate_->DidCompleteRecognition(caller_id_);
}

void SpeechRecognizer::InformErrorAndCancelRecognition(ErrorCode error) {
  CancelRecognition();

  // Guard against the delegate freeing us until we finish our job.
  scoped_refptr<SpeechRecognizer> me(this);
  delegate_->OnRecognizerError(caller_id_, error);
}

}  // namespace speech_input
<commit_msg>Switch from using Speex to FLAC for speech input requests.<commit_after>// Copyright (c) 2011 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/browser/speech/speech_recognizer.h"

#include "base/time.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/net/url_request_context_getter.h"
#include "content/browser/browser_thread.h"

using media::AudioInputController;
using std::string;

namespace {

// The following constants are related to the volume level indicator shown in
// the UI for recorded audio.
// Multiplier used when new volume is greater than previous level.
const float kUpSmoothingFactor = 1.0f;
// Multiplier used when new volume is lesser than previous level.
const float kDownSmoothingFactor = 0.7f;
// RMS dB value of a maximum (unclipped) sine wave for int16 samples.
const float kAudioMeterMaxDb = 90.31f;
// This value corresponds to RMS dB for int16 with 6 most-significant-bits = 0.
// Values lower than this will display as empty level-meter.
const float kAudioMeterMinDb = 30.0f;
const float kAudioMeterDbRange = kAudioMeterMaxDb - kAudioMeterMinDb;

// Maximum level to draw to display unclipped meter. (1.0f displays clipping.)
const float kAudioMeterRangeMaxUnclipped = 47.0f / 48.0f;

// Returns true if more than 5% of the samples are at min or max value.
bool Clipping(const int16* samples, int num_samples) {
  int clipping_samples = 0;
  const int kThreshold = num_samples / 20;
  for (int i = 0; i < num_samples; ++i) {
    if (samples[i] <= -32767 || samples[i] >= 32767) {
      if (++clipping_samples > kThreshold)
        return true;
    }
  }
  return false;
}

}  // namespace

namespace speech_input {

const int SpeechRecognizer::kAudioSampleRate = 16000;
const int SpeechRecognizer::kAudioPacketIntervalMs = 100;
const int SpeechRecognizer::kNumAudioChannels = 1;
const int SpeechRecognizer::kNumBitsPerAudioSample = 16;
const int SpeechRecognizer::kNoSpeechTimeoutSec = 8;
const int SpeechRecognizer::kEndpointerEstimationTimeMs = 300;

SpeechRecognizer::SpeechRecognizer(Delegate* delegate,
                                   int caller_id,
                                   const std::string& language,
                                   const std::string& grammar,
                                   const std::string& hardware_info,
                                   const std::string& origin_url)
    : delegate_(delegate),
      caller_id_(caller_id),
      language_(language),
      grammar_(grammar),
      hardware_info_(hardware_info),
      origin_url_(origin_url),
      codec_(AudioEncoder::CODEC_FLAC),
      encoder_(NULL),
      endpointer_(kAudioSampleRate),
      num_samples_recorded_(0),
      audio_level_(0.0f) {
  endpointer_.set_speech_input_complete_silence_length(
      base::Time::kMicrosecondsPerSecond / 2);
  endpointer_.set_long_speech_input_complete_silence_length(
      base::Time::kMicrosecondsPerSecond);
  endpointer_.set_long_speech_length(3 * base::Time::kMicrosecondsPerSecond);
  endpointer_.StartSession();
}

SpeechRecognizer::~SpeechRecognizer() {
  // Recording should have stopped earlier due to the endpointer or
  // |StopRecording| being called.
  DCHECK(!audio_controller_.get());
  DCHECK(!request_.get() || !request_->HasPendingRequest());
  DCHECK(!encoder_.get());
  endpointer_.EndSession();
}

bool SpeechRecognizer::StartRecording() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  DCHECK(!audio_controller_.get());
  DCHECK(!request_.get() || !request_->HasPendingRequest());
  DCHECK(!encoder_.get());

  // The endpointer needs to estimate the environment/background noise before
  // starting to treat the audio as user input. In |HandleOnData| we wait until
  // such time has passed before switching to user input mode.
  endpointer_.SetEnvironmentEstimationMode();

  encoder_.reset(AudioEncoder::Create(codec_, kAudioSampleRate,
                                      kNumBitsPerAudioSample));
  int samples_per_packet = (kAudioSampleRate * kAudioPacketIntervalMs) / 1000;
  AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, kNumAudioChannels,
                         kAudioSampleRate, kNumBitsPerAudioSample,
                         samples_per_packet);
  audio_controller_ = AudioInputController::Create(this, params);
  DCHECK(audio_controller_.get());
  VLOG(1) << "SpeechRecognizer starting record.";
  num_samples_recorded_ = 0;
  audio_controller_->Record();

  return true;
}

void SpeechRecognizer::CancelRecognition() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  DCHECK(audio_controller_.get() || request_.get());

  // Stop recording if required.
  if (audio_controller_.get()) {
    VLOG(1) << "SpeechRecognizer stopping record.";
    audio_controller_->Close();
    audio_controller_ = NULL;  // Releases the ref ptr.
  }

  VLOG(1) << "SpeechRecognizer canceling recognition.";
  encoder_.reset();
  request_.reset();
}

void SpeechRecognizer::StopRecording() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));

  // If audio recording has already stopped and we are in recognition phase,
  // silently ignore any more calls to stop recording.
  if (!audio_controller_.get())
    return;

  VLOG(1) << "SpeechRecognizer stopping record.";
  audio_controller_->Close();
  audio_controller_ = NULL;  // Releases the ref ptr.

  delegate_->DidCompleteRecording(caller_id_);

  // UploadAudioChunk requires a non-empty final buffer. So we encode a packet
  // of silence in case encoder had no data already.
  std::vector<short> samples((kAudioSampleRate * kAudioPacketIntervalMs) /
                             1000);
  encoder_->Encode(&samples[0], samples.size());
  encoder_->Flush();
  string encoded_data;
  encoder_->GetEncodedDataAndClear(&encoded_data);
  DCHECK(!encoded_data.empty());
  encoder_.reset();

  // If we haven't got any audio yet end the recognition sequence here.
  if (request_ == NULL) {
    // Guard against the delegate freeing us until we finish our job.
    scoped_refptr<SpeechRecognizer> me(this);
    delegate_->DidCompleteRecognition(caller_id_);
  } else {
    request_->UploadAudioChunk(encoded_data, true /* is_last_chunk */);
  }
}

// Invoked in the audio thread.
void SpeechRecognizer::OnError(AudioInputController* controller,
                               int error_code) {
  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
                         NewRunnableMethod(this,
                                           &SpeechRecognizer::HandleOnError,
                                           error_code));
}

void SpeechRecognizer::HandleOnError(int error_code) {
  LOG(WARNING) << "SpeechRecognizer::HandleOnError, code=" << error_code;

  // Check if we are still recording before canceling recognition, as
  // recording might have been stopped after this error was posted to the queue
  // by |OnError|.
  if (!audio_controller_.get())
    return;

  InformErrorAndCancelRecognition(RECOGNIZER_ERROR_CAPTURE);
}

void SpeechRecognizer::OnData(AudioInputController* controller,
                              const uint8* data, uint32 size) {
  if (size == 0)  // This could happen when recording stops and is normal.
    return;

  string* str_data = new string(reinterpret_cast<const char*>(data), size);
  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
                         NewRunnableMethod(this,
                                           &SpeechRecognizer::HandleOnData,
                                           str_data));
}

void SpeechRecognizer::HandleOnData(string* data) {
  // Check if we are still recording and if not discard this buffer, as
  // recording might have been stopped after this buffer was posted to the queue
  // by |OnData|.
  if (!audio_controller_.get()) {
    delete data;
    return;
  }

  const short* samples = reinterpret_cast<const short*>(data->data());
  DCHECK((data->length() % sizeof(short)) == 0);
  int num_samples = data->length() / sizeof(short);
  encoder_->Encode(samples, num_samples);
  float rms;
  endpointer_.ProcessAudio(samples, num_samples, &rms);
  bool did_clip = Clipping(samples, num_samples);
  delete data;
  num_samples_recorded_ += num_samples;

  if (request_ == NULL) {
    // This was the first audio packet recorded, so start a request to the
    // server to send the data.
    request_.reset(new SpeechRecognitionRequest(
        Profile::GetDefaultRequestContext(), this));
    request_->Start(language_, grammar_, hardware_info_, origin_url_,
                    encoder_->mime_type());
  }

  string encoded_data;
  encoder_->GetEncodedDataAndClear(&encoded_data);
  DCHECK(!encoded_data.empty());
  request_->UploadAudioChunk(encoded_data, false /* is_last_chunk */);

  if (endpointer_.IsEstimatingEnvironment()) {
    // Check if we have gathered enough audio for the endpointer to do
    // environment estimation and should move on to detect speech/end of speech.
    if (num_samples_recorded_ >= (kEndpointerEstimationTimeMs *
                                  kAudioSampleRate) / 1000) {
      endpointer_.SetUserInputMode();
      delegate_->DidCompleteEnvironmentEstimation(caller_id_);
    }
    return;  // No more processing since we are still estimating environment.
  }

  // Check if we have waited too long without hearing any speech.
  if (!endpointer_.DidStartReceivingSpeech() &&
      num_samples_recorded_ >= kNoSpeechTimeoutSec * kAudioSampleRate) {
    InformErrorAndCancelRecognition(RECOGNIZER_ERROR_NO_SPEECH);
    return;
  }

  // Calculate the input volume to display in the UI, smoothing towards the
  // new level.
  float level = (rms - kAudioMeterMinDb) /
      (kAudioMeterDbRange / kAudioMeterRangeMaxUnclipped);
  level = std::min(std::max(0.0f, level), kAudioMeterRangeMaxUnclipped);
  if (level > audio_level_) {
    audio_level_ += (level - audio_level_) * kUpSmoothingFactor;
  } else {
    audio_level_ += (level - audio_level_) * kDownSmoothingFactor;
  }

  float noise_level = (endpointer_.NoiseLevelDb() - kAudioMeterMinDb) /
      (kAudioMeterDbRange / kAudioMeterRangeMaxUnclipped);
  noise_level = std::min(std::max(0.0f, noise_level),
      kAudioMeterRangeMaxUnclipped);

  delegate_->SetInputVolume(caller_id_, did_clip ? 1.0f : audio_level_,
                            noise_level);

  if (endpointer_.speech_input_complete()) {
    StopRecording();
  }

  // TODO(satish): Once we have streaming POST, start sending the data received
  // here as POST chunks.
}

void SpeechRecognizer::SetRecognitionResult(
    bool error, const SpeechInputResultArray& result) {
  if (error || result.empty()) {
    InformErrorAndCancelRecognition(error ? RECOGNIZER_ERROR_NETWORK :
                                            RECOGNIZER_ERROR_NO_RESULTS);
    return;
  }

  delegate_->SetRecognitionResult(caller_id_, error, result);

  // Guard against the delegate freeing us until we finish our job.
  scoped_refptr<SpeechRecognizer> me(this);
  delegate_->DidCompleteRecognition(caller_id_);
}

void SpeechRecognizer::InformErrorAndCancelRecognition(ErrorCode error) {
  CancelRecognition();

  // Guard against the delegate freeing us until we finish our job.
  scoped_refptr<SpeechRecognizer> me(this);
  delegate_->OnRecognizerError(caller_id_, error);
}

}  // namespace speech_input
<|endoftext|>


<commit_before>/* This file is part of Strigi Desktop Search
 *
 * Copyright (C) 2006 Jos van den Oever <jos@vandenoever.info>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */
#include "base64inputstream.h"

#include <cstring>

using namespace std;
using namespace Strigi;

/* code is based on public domain code at
   http://www.tug.org/ftp/vm/base64-decode.c
*/

class Base64InputStream::Private {
public:
    Base64InputStream* const p;
    InputStream* input;
    const char* pos, * pend;
    int32_t bits;
    int32_t nleft;
    char bytestodo;
    char char_count;

    Private(Base64InputStream* p, InputStream* i);
    bool moreData();
    int32_t fillBuffer(char* start, int32_t space);
};

const unsigned char alphabet[]
    = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
bool inalphabet[256];
unsigned char decoder[133];
bool initializedAlphabet = false;
void initialize();
string decode(const char* in, string::size_type length);

void
initialize() {
    if (!initializedAlphabet) {
        initializedAlphabet = true;
        // initialize the translation arrays
        for (int i=64; i<256; ++i) {
            inalphabet[i] = 0;
        }
        for (int i=0; i<64; ++i) {
            inalphabet[alphabet[i]] = true;
            decoder[alphabet[i]] = i;
        }
    }
}

Base64InputStream::Base64InputStream(InputStream* i) :p(new Private(this, i)) {
}
Base64InputStream::~Base64InputStream() {
    delete p;
}
Base64InputStream::Private::Private(Base64InputStream* bi, InputStream* i)
        :p(bi), input(i) {
    initialize();
    nleft = 0;
    char_count = 0;
    bits = 0;
    bytestodo = 0;
    pos = pend = 0;
}
bool
Base64InputStream::Private::moreData() {
    if (pos == pend) {
        int32_t nread = input->read(pos, 1, 0);
        if (nread < -1) {
            p->m_status = Error;
            p->m_error = input->error();
            input = 0;
            return false;
        }
        if (nread <= 0) {
            input = 0;
            return false;
        }
        pend = pos + nread;
    }
    return true;
}
int32_t
Base64InputStream::fillBuffer(char* start, int32_t space) {
    return p->fillBuffer(start, space);
}
int32_t
Base64InputStream::Private::fillBuffer(char* start, int32_t space) {
    if (input == 0 && bytestodo == 0) return -1;
    // handle the  bytes that were left over from the last call
    if (bytestodo) {
        switch (bytestodo) {
        case 3:
            *start = bits >> 16;
            break;
        case 2:
            *start = (bits >> 8) & 0xff;
            break;
        case 1:
            *start = bits & 0xff;
            bits = 0;
            char_count = 0;
            break;
        }
        bytestodo--;
        return 1;
    }
    const char* end = start + space;
    char* p = start;
    int32_t nwritten = 0;
    while (moreData()) {
        unsigned char c = *pos++;
        // = signals the end of the encoded block
        if (c == '=') {
            if (char_count == 2) {
                bytestodo = 1;
                bits >>= 10;
            } else if (char_count == 3) {
                bytestodo = 2;
                bits >>= 8;
            }
            input = 0;
            break;
        }
        // we ignore characters that do not fit
        if (!inalphabet[c]) {
            continue;
        }
        bits += decoder[c];
        char_count++;
        if (char_count == 4) {
            if (p >= end) {
                bytestodo = 3;
                break;
            }
            *p++ = bits >> 16;
            if (p >= end) {
                bytestodo = 2;
                nwritten++;
                break;
            }
            *p++ = (bits >> 8) & 0xff;
            if (p >= end) {
                bytestodo = 1;
                nwritten += 2;
                break;
            }
            *p++ = bits & 0xff;
            bits = 0;
            char_count = 0;
            nwritten += 3;
        } else {
            bits <<= 6;
        }
    }
    if (nwritten == 0 && input == 0 && bytestodo == 0) {
//        printf("EOF\n");
        nwritten = -1;
    }
    return nwritten;
}
string
Base64InputStream::decode(const char* in, string::size_type length) {
    initialize();
    string d;
    if (length%4) return d;
    initialize();
    int32_t words = length/4;
    d.reserve(words*3);
    const unsigned char* c = (const unsigned char*)in;
    const unsigned char* e = c + length;
    if (in[length-1] == '=') {
        e -= 4;
    }
    char k, l, b[3];
    for (; c < e; c += 4) {
         if (inalphabet[c[0]]  && inalphabet[c[1]] && inalphabet[c[2]]
             && inalphabet[c[3]]) {
            k = decoder[c[1]];
            l = decoder[c[2]];
            b[0] = (decoder[c[0]] << 2) + (k >> 4);
            b[1] = (k             << 4) + (l >> 2);
            b[2] = (l             << 6) + (decoder[c[3]]);
            d.append(b, 3);
         } else {
             return string();
         }
    }
    if (in[length-2] == '=') {
        if (inalphabet[c[0]]  && inalphabet[c[1]]) {
            b[0] = (decoder[c[0]] << 2)+((decoder[c[1]] >> 4));
            d.append(b, 1);
        } else {
            return string();
        }
    } else if (in[length-1] == '=') {
        if (inalphabet[c[0]]  && inalphabet[c[1]] && inalphabet[c[2]]) {
            k = decoder[c[1]];
            b[0] = (decoder[c[0]] << 2) + (k >> 4);
            b[1] = (k             << 4) + (decoder[c[2]] >> 2);
            d.append(b, 2);
        } else {
            return string();
        }
    }
    return d;
}
<commit_msg>fix memory leak (CID 4060)<commit_after>/* This file is part of Strigi Desktop Search
 *
 * Copyright (C) 2006 Jos van den Oever <jos@vandenoever.info>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */
#include "base64inputstream.h"

#include <cstring>

using namespace std;
using namespace Strigi;

/* code is based on public domain code at
   http://www.tug.org/ftp/vm/base64-decode.c
*/

class Base64InputStream::Private {
public:
    Base64InputStream* const p;
    InputStream* input;
    const char* pos, * pend;
    int32_t bits;
    int32_t nleft;
    char bytestodo;
    char char_count;

    Private(Base64InputStream* p, InputStream* i);
    bool moreData();
    int32_t fillBuffer(char* start, int32_t space);
};

const unsigned char alphabet[]
    = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
bool inalphabet[256];
unsigned char decoder[133];
bool initializedAlphabet = false;
void initialize();
string decode(const char* in, string::size_type length);

void
initialize() {
    if (!initializedAlphabet) {
        initializedAlphabet = true;
        // initialize the translation arrays
        for (int i=64; i<256; ++i) {
            inalphabet[i] = 0;
        }
        for (int i=0; i<64; ++i) {
            inalphabet[alphabet[i]] = true;
            decoder[alphabet[i]] = i;
        }
    }
}

Base64InputStream::Base64InputStream(InputStream* i)
    :p(new Private(this, i))
{
}

Base64InputStream::~Base64InputStream() {
    delete p;
}
Base64InputStream::Private::Private(Base64InputStream* bi, InputStream* i)
        :p(bi), input(i) {
    initialize();
    nleft = 0;
    char_count = 0;
    bits = 0;
    bytestodo = 0;
    pos = pend = 0;
}
bool
Base64InputStream::Private::moreData() {
    if (pos == pend) {
        int32_t nread = input->read(pos, 1, 0);
        if (nread < -1) {
            p->m_status = Error;
            p->m_error = input->error();
            input = 0;
            return false;
        }
        if (nread <= 0) {
            input = 0;
            return false;
        }
        pend = pos + nread;
    }
    return true;
}
int32_t
Base64InputStream::fillBuffer(char* start, int32_t space) {
    return p->fillBuffer(start, space);
}
int32_t
Base64InputStream::Private::fillBuffer(char* start, int32_t space) {
    if (input == 0 && bytestodo == 0) return -1;
    // handle the  bytes that were left over from the last call
    if (bytestodo) {
        switch (bytestodo) {
        case 3:
            *start = bits >> 16;
            break;
        case 2:
            *start = (bits >> 8) & 0xff;
            break;
        case 1:
            *start = bits & 0xff;
            bits = 0;
            char_count = 0;
            break;
        }
        bytestodo--;
        return 1;
    }
    const char* end = start + space;
    char* p = start;
    int32_t nwritten = 0;
    while (moreData()) {
        unsigned char c = *pos++;
        // = signals the end of the encoded block
        if (c == '=') {
            if (char_count == 2) {
                bytestodo = 1;
                bits >>= 10;
            } else if (char_count == 3) {
                bytestodo = 2;
                bits >>= 8;
            }
            input = 0;
            break;
        }
        // we ignore characters that do not fit
        if (!inalphabet[c]) {
            continue;
        }
        bits += decoder[c];
        char_count++;
        if (char_count == 4) {
            if (p >= end) {
                bytestodo = 3;
                break;
            }
            *p++ = bits >> 16;
            if (p >= end) {
                bytestodo = 2;
                nwritten++;
                break;
            }
            *p++ = (bits >> 8) & 0xff;
            if (p >= end) {
                bytestodo = 1;
                nwritten += 2;
                break;
            }
            *p++ = bits & 0xff;
            bits = 0;
            char_count = 0;
            nwritten += 3;
        } else {
            bits <<= 6;
        }
    }
    if (nwritten == 0 && input == 0 && bytestodo == 0) {
//        printf("EOF\n");
        nwritten = -1;
    }
    return nwritten;
}
string
Base64InputStream::decode(const char* in, string::size_type length) {
    initialize();
    string d;
    if (length%4) return d;
    initialize();
    int32_t words = length/4;
    d.reserve(words*3);
    const unsigned char* c = (const unsigned char*)in;
    const unsigned char* e = c + length;
    if (in[length-1] == '=') {
        e -= 4;
    }
    char k, l, b[3];
    for (; c < e; c += 4) {
         if (inalphabet[c[0]]  && inalphabet[c[1]] && inalphabet[c[2]]
             && inalphabet[c[3]]) {
            k = decoder[c[1]];
            l = decoder[c[2]];
            b[0] = (decoder[c[0]] << 2) + (k >> 4);
            b[1] = (k             << 4) + (l >> 2);
            b[2] = (l             << 6) + (decoder[c[3]]);
            d.append(b, 3);
         } else {
             return string();
         }
    }
    if (in[length-2] == '=') {
        if (inalphabet[c[0]]  && inalphabet[c[1]]) {
            b[0] = (decoder[c[0]] << 2)+((decoder[c[1]] >> 4));
            d.append(b, 1);
        } else {
            return string();
        }
    } else if (in[length-1] == '=') {
        if (inalphabet[c[0]]  && inalphabet[c[1]] && inalphabet[c[2]]) {
            k = decoder[c[1]];
            b[0] = (decoder[c[0]] << 2) + (k >> 4);
            b[1] = (k             << 4) + (decoder[c[2]] >> 2);
            d.append(b, 2);
        } else {
            return string();
        }
    }
    return d;
}
<|endoftext|>