diff --git a/web_domain_field/README.rst b/web_domain_field/README.rst
new file mode 100644
index 000000000..e70f68f28
--- /dev/null
+++ b/web_domain_field/README.rst
@@ -0,0 +1,104 @@
+.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
+   :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
+   :alt: License: AGPL-3
+
+================
+Web Domain Field
+================
+
+When you define a view you can specify on the relational fields a domain
+attribute. This attribute is evaluated as filter to apply when displaying
+existing records for selection.
+
+.. code-block:: xml
+
+   <field name="product_id" domain="[('type','=','product')]"/>
+
+The value provided for the domain attribute must be a string representing a
+valid Odoo domain. This string is evaluated on the client side in a
+restricted context where we can reference as right operand the values of
+fields present into the form and a limited set of functions.
+
+In this context it's hard to build complex domain and we are facing to some
+limitations as:
+
+ * The syntax to include in your domain a criteria involving values from a
+   x2many field is complex.
+ * Domains computed by an onchange on an other field are not recomputed when
+   you modify the form and don't modify the field triggering the onchange.
+ * It's not possible to extend an existing domain. You must completely redefine
+   the domain in your specialized addon
+ * ...
+
+In order to mitigate these limitations this new addon allows you to use the
+value of a field as domain of an other field in the xml definition of your
+view.
+
+.. code-block:: xml
+
+   <field name="product_id_domain" invisible="1"/>
+   <field name="product_id" domain="product_id_domain"/>
+
+The field used as domain must provide the domain as a JSON encoded string.
+
+.. code-block:: python
+
+   product_id_domain = fields.Char(
+       compute="_compute_product_id_domain",
+       readonly=True,
+       store=False,
+   )
+
+   @api.multi
+   @api.depends('name')
+   def _compute_product_id_domain(self):
+       for rec in self:
+           rec.product_id_domain = json.dumps(
+               [('type', '=', 'product'), ('name', 'like', rec.name)]
+           )
+
+
+Usage
+=====
+
+.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
+   :alt: Try me on Runbot
+   :target: https://runbot.odoo-community.org/runbot/162/9.0
+
+
+
+Bug Tracker
+===========
+
+Bugs are tracked on `GitHub Issues
+<https://github.com/OCA/web/issues>`_. In case of trouble, please
+check there if your issue has already been reported. If you spotted it first,
+help us smashing it by providing a detailed and welcomed feedback.
+
+Credits
+=======
+
+Images
+------
+
+* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
+
+Contributors
+------------
+
+* Laurent Mignon <laurent.mignon@acsone.eu>
+
+Maintainer
+----------
+
+.. image:: https://odoo-community.org/logo.png
+   :alt: Odoo Community Association
+   :target: https://odoo-community.org
+
+This module is maintained by the OCA.
+
+OCA, or the Odoo Community Association, is a nonprofit organization whose
+mission is to support the collaborative development of Odoo features and
+promote its widespread use.
+
+To contribute to this module, please visit https://odoo-community.org.
diff --git a/web_domain_field/__init__.py b/web_domain_field/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/web_domain_field/__openerp__.py b/web_domain_field/__openerp__.py
new file mode 100644
index 000000000..4ff66a1c7
--- /dev/null
+++ b/web_domain_field/__openerp__.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Copyright 2017 ACSONE SA/NV
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+{
+    'name': 'Web Domain Field',
+    'summary': """
+        Use computed field as domain""",
+    'version': '9.0.1.0.0',
+    'license': 'AGPL-3',
+    'author': 'ACSONE SA/NV,Odoo Community Association (OCA)',
+    'website': 'https://acsone.eu/',
+    'depends': [
+        'web'
+    ],
+    'data': [
+        'views/web_domain_field.xml',
+    ],
+    'demo': [
+    ],
+}
diff --git a/web_domain_field/static/description/icon.png b/web_domain_field/static/description/icon.png
new file mode 100644
index 000000000..3a0328b51
Binary files /dev/null and b/web_domain_field/static/description/icon.png differ
diff --git a/web_domain_field/static/src/js/pyeval.js b/web_domain_field/static/src/js/pyeval.js
new file mode 100644
index 000000000..f4b75e579
--- /dev/null
+++ b/web_domain_field/static/src/js/pyeval.js
@@ -0,0 +1,233 @@
+odoo.define('web.domain_field', function (require) {
+"use strict";
+
+var pyeval = require('web.pyeval');
+var session = require('web.session');
+
+
+var original_pyeval = pyeval.eval;
+var original_ensure_evaluated = pyeval.ensure_evaluated;
+var py = window.py;
+
+/** copied from pyeval and not modified but required since not publicly
+exposed by web.pyeval**/
+
+// recursively wraps JS objects passed into the context to attributedicts
+// which jsonify back to JS objects
+function wrap(value) {
+    if (value === null) { return py.None; }
+
+    switch (typeof value) {
+    case 'undefined': throw new Error("No conversion for undefined");
+    case 'boolean': return py.bool.fromJSON(value);
+    case 'number': return py.float.fromJSON(value);
+    case 'string': return py.str.fromJSON(value);
+    }
+
+    switch(value.constructor) {
+    case Object: return wrapping_dict.fromJSON(value);
+    case Array: return wrapping_list.fromJSON(value);
+    }
+
+    throw new Error("ValueError: unable to wrap " + value);
+}
+
+var wrapping_dict = py.type('wrapping_dict', null, {
+    __init__: function () {
+        this._store = {};
+    },
+    __getitem__: function (key) {
+        var k = key.toJSON();
+        if (!(k in this._store)) {
+            throw new Error("KeyError: '" + k + "'");
+        }
+        return wrap(this._store[k]);
+    },
+    __getattr__: function (key) {
+        return this.__getitem__(py.str.fromJSON(key));
+    },
+    __len__: function () {
+        return Object.keys(this._store).length
+    },
+    __nonzero__: function () {
+        return py.PY_size(this) > 0 ? py.True : py.False;
+    },
+    get: function () {
+        var args = py.PY_parseArgs(arguments, ['k', ['d', py.None]]);
+
+        if (!(args.k.toJSON() in this._store)) { return args.d; }
+        return this.__getitem__(args.k);
+    },
+    fromJSON: function (d) {
+        var instance = py.PY_call(wrapping_dict);
+        instance._store = d;
+        return instance;
+    },
+    toJSON: function () {
+        return this._store;
+    },
+});
+
+var wrapping_list = py.type('wrapping_list', null, {
+    __init__: function () {
+        this._store = [];
+    },
+    __getitem__: function (index) {
+        return wrap(this._store[index.toJSON()]);
+    },
+    __len__: function () {
+        return this._store.length;
+    },
+    __nonzero__: function () {
+        return py.PY_size(this) > 0 ? py.True : py.False;
+    },
+    fromJSON: function (ar) {
+        var instance = py.PY_call(wrapping_list);
+        instance._store = ar;
+        return instance;
+    },
+    toJSON: function () {
+        return this._store;
+    },
+});
+
+function wrap_context (context) {
+    for (var k in context) {
+        if (!context.hasOwnProperty(k)) { continue; }
+        var val = context[k];
+
+        if (val === null) { continue; }
+        if (val.constructor === Array) {
+            context[k] = wrapping_list.fromJSON(val);
+        } else if (val.constructor === Object
+                   && !py.PY_isInstance(val, py.object)) {
+            context[k] = wrapping_dict.fromJSON(val);
+        }
+    }
+    return context;
+}
+
+function ensure_evaluated (args, kwargs) {
+    for (var i=0; i<args.length; ++i) {
+        args[i] = eval_arg(args[i]);
+    }
+    for (var k in kwargs) {
+        if (!kwargs.hasOwnProperty(k)) { continue; }
+        kwargs[k] = eval_arg(kwargs[k]);
+    }
+}
+function eval_contexts (contexts, evaluation_context) {
+    evaluation_context = _.extend(pyeval.context(), evaluation_context || {});
+    return _(contexts).reduce(function (result_context, ctx) {
+        // __eval_context evaluations can lead to some of `contexts`'s
+        // values being null, skip them as well as empty contexts
+        if (_.isEmpty(ctx)) { return result_context; }
+        if (_.isString(ctx)) {
+            // wrap raw strings in context
+            ctx = { __ref: 'context', __debug: ctx };
+        }
+        var evaluated = ctx;
+        switch(ctx.__ref) {
+        case 'context':
+            evaluation_context.context = evaluation_context;
+            evaluated = py.eval(ctx.__debug, wrap_context(evaluation_context));
+            break;
+        case 'compound_context':
+            var eval_context = eval_contexts([ctx.__eval_context]);
+            evaluated = eval_contexts(
+                ctx.__contexts, _.extend({}, evaluation_context, eval_context));
+            break;
+        }
+        // add newly evaluated context to evaluation context for following
+        // siblings
+        _.extend(evaluation_context, evaluated);
+        return _.extend(result_context, evaluated);
+    }, {});
+}
+
+/** end of unmodified methods copied from pyeval **/
+
+// We need to override the original method to be able to call our
+//specialized version of pyeval for domain fields
+function eval_arg (arg) {
+    if (typeof arg !== 'object' || !arg.__ref) { return arg; }
+    switch(arg.__ref) {
+    case 'domain': case 'compound_domain':
+        return domain_field_pyeval('domains', [arg]);
+    case 'context': case 'compound_context':
+        return original_pyeval('contexts', [arg]);
+    default:
+        throw new Error(_t("Unknown nonliteral type ") + ' ' + arg.__ref);
+    }
+}
+
+// override eval_domains to add 3 lines in order to be able to use a field
+//value as domain
+function eval_domains (domains, evaluation_context) {
+    evaluation_context = _.extend(pyeval.context(), evaluation_context ||
+    {});
+    var result_domain = [];
+    _(domains).each(function (domain) {
+        if (_.isString(domain)) {
+           // Modified part or the original method
+           if(domain in evaluation_context) {
+                result_domain.push.apply(
+                    result_domain, $.parseJSON(evaluation_context[domain]));
+                return;
+           }
+           // end of modifications
+
+           // wrap raw strings in domain
+           domain = { __ref: 'domain', __debug: domain };
+        }
+        switch(domain.__ref) {
+        case 'domain':
+            evaluation_context.context = evaluation_context;
+            result_domain.push.apply(
+                result_domain, py.eval(domain.__debug, wrap_context(evaluation_context)));
+            break;
+        case 'compound_domain':
+            var eval_context = eval_contexts([domain.__eval_context]);
+            result_domain.push.apply(
+                result_domain, eval_domains(
+                    domain.__domains, _.extend(
+                        {}, evaluation_context, eval_context)));
+            break;
+        default:
+            result_domain.push.apply(result_domain, domain);
+        }
+    });
+    return result_domain;
+}
+
+// override pyeval in order to call our specialized implementation of
+// eval_domains
+function domain_field_pyeval (type, object, context, options) {
+    switch(type) {
+    case 'domain':
+    case 'domains':
+        if (type === 'domain')
+            object = [object];
+        return eval_domains(object, context);
+    default:
+        return original_pyeval(type, object, context, options);
+     }
+}
+
+// override sync_eval in order to call our specialized implementation of
+// eval_domains
+function sync_eval_domains_and_contexts (source) {
+    var contexts = ([session.user_context] || []).concat(source.contexts);
+    // see Session.eval_context in Python
+    return {
+        context: domain_field_pyeval('contexts', contexts),
+        domain: domain_field_pyeval('domains', source.domains),
+        group_by: domain_field_pyeval('groupbys', source.group_by_seq || [])
+    };
+}
+
+pyeval.eval = domain_field_pyeval;
+pyeval.ensure_evaluated = ensure_evaluated;
+pyeval.sync_eval_domains_and_contexts = sync_eval_domains_and_contexts;
+
+});
\ No newline at end of file
diff --git a/web_domain_field/views/web_domain_field.xml b/web_domain_field/views/web_domain_field.xml
new file mode 100644
index 000000000..537d2f874
--- /dev/null
+++ b/web_domain_field/views/web_domain_field.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <template id="assets_backend" name="web_domain_field assets" inherit_id="web.assets_backend">
+        <xpath expr="script[last()]" position="after">
+            <script type="text/javascript" src="/web_domain_field/static/src/js/pyeval.js" />
+        </xpath>
+    </template>
+</odoo>
\ No newline at end of file