From 11f00f17dc81398927a6978d3acb1f57e32609b8 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 27 Dec 2012 11:12:06 -0500 Subject: [PATCH] Bug 816730 - [Activities] Filters do not work anymore. r=mounir, sr=sicking --- dom/activities/Makefile.in | 3 + dom/activities/src/ActivitiesService.jsm | 36 +---- .../src/ActivitiesServiceFilter.jsm | 127 +++++++++++++++ dom/activities/src/Makefile.in | 1 + dom/activities/tests/Makefile.in | 17 ++ .../tests/unit/test_activityFilters.js | 151 ++++++++++++++++++ dom/activities/tests/unit/xpcshell.ini | 5 + testing/xpcshell/xpcshell.ini | 1 + 8 files changed, 308 insertions(+), 33 deletions(-) create mode 100644 dom/activities/src/ActivitiesServiceFilter.jsm create mode 100644 dom/activities/tests/Makefile.in create mode 100644 dom/activities/tests/unit/test_activityFilters.js create mode 100644 dom/activities/tests/unit/xpcshell.ini diff --git a/dom/activities/Makefile.in b/dom/activities/Makefile.in index 51691b343dd9..db4db0d89bd1 100644 --- a/dom/activities/Makefile.in +++ b/dom/activities/Makefile.in @@ -6,9 +6,12 @@ DEPTH = @DEPTH@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ +relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk PARALLEL_DIRS = interfaces src +TEST_DIRS += tests + include $(topsrcdir)/config/rules.mk diff --git a/dom/activities/src/ActivitiesService.jsm b/dom/activities/src/ActivitiesService.jsm index 2ca899badb12..e8bad41a3713 100644 --- a/dom/activities/src/ActivitiesService.jsm +++ b/dom/activities/src/ActivitiesService.jsm @@ -11,6 +11,7 @@ const Ci = Components.interfaces; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/IndexedDBHelper.jsm"); +Cu.import("resource://gre/modules/ActivitiesServiceFilter.jsm"); XPCOMUtils.defineLazyServiceGetter(this, "ppmm", "@mozilla.org/parentprocessmessagemanager;1", @@ -258,39 +259,8 @@ let Activities = { }; let matchFunc = function matchFunc(aResult) { - - function matchFuncValue(aValue, aFilter) { - // Bug 805822 - Regexp support for MozActivity - - let values = Array.isArray(aValue) ? aValue : [aValue]; - let filters = Array.isArray(aFilter) ? aFilter : [aFilter]; - - // At least 1 value must match. - let ret = false; - values.forEach(function(value) { - if (filters.indexOf(value) != -1) { - ret = true; - } - }); - - return ret; - } - - // For any incoming property. - for (let prop in aMsg.options.data) { - - // If this is unknown for the app, let's continue. - if (!(prop in aResult.description.filters)) { - continue; - } - - // Otherwise, let's check the value against the filter. - if (!matchFuncValue(aMsg.options.data[prop], aResult.description.filters[prop])) { - return false; - } - } - - return true; + return ActivitiesServiceFilter.match(aMsg.options.data, + aResult.description.filters); }; this.db.find(aMsg, successCb, errorCb, matchFunc); diff --git a/dom/activities/src/ActivitiesServiceFilter.jsm b/dom/activities/src/ActivitiesServiceFilter.jsm new file mode 100644 index 000000000000..5107a468fdd6 --- /dev/null +++ b/dom/activities/src/ActivitiesServiceFilter.jsm @@ -0,0 +1,127 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict" + +this.EXPORTED_SYMBOLS = ['ActivitiesServiceFilter']; + +this.ActivitiesServiceFilter = { + match: function(aValues, aFilters) { + + function matchValue(aValue, aFilter, aFilterObj) { + if (aFilter !== null) { + // Custom functions for the different types. + switch (typeof(aFilter)) { + case 'boolean': + return aValue === aFilter; + + case 'number': + return Number(aValue) === aFilter; + + case 'string': + return String(aValue) === aFilter; + + default: // not supported + return false; + } + } + + // Regexp. + if (('regexp' in aFilterObj)) { + var regexp = String(aFilterObj.regexp); + + if (regexp[0] != "/") + return false; + + var pos = regexp.lastIndexOf("/"); + if (pos == 0) + return false; + + var re = new RegExp(regexp.substring(1, pos), regexp.substr(pos + 1)); + return re.test(aValue); + } + + // Validation of the min/Max. + if (('min' in aFilterObj) || ('max' in aFilterObj)) { + // Min value. + if (('min' in aFilterObj) && + aFilterObj.min > aValue) { + return false; + } + + // Max value. + if (('max' in aFilterObj) && + aFilterObj.max < aValue) { + return false; + } + } + + return true; + } + + // this function returns true if the value matches with the filter object + function matchObject(aValue, aFilterObj) { + + // Let's consider anything an array. + let filters = ('value' in aFilterObj) + ? (Array.isArray(aFilterObj.value) + ? aFilterObj.value + : [aFilterObj.value]) + : [ null ]; + let values = Array.isArray(aValue) ? aValue : [aValue]; + + for (var filterId = 0; filterId < filters.length; ++filterId) { + for (var valueId = 0; valueId < values.length; ++valueId) { + if (matchValue(values[valueId], filters[filterId], aFilterObj)) { + return true; + } + } + } + + return false; + } + + // Creation of a filter map useful to know what has been + // matched and what is not. + let filtersMap = {} + for (let filter in aFilters) { + // Convert this filter in an object if needed + let filterObj = aFilters[filter]; + + if (Array.isArray(filterObj) || typeof(filterObj) !== 'object') { + filterObj = { + required: false, + value: filterObj + } + } + + filtersMap[filter] = { filter: filterObj, + found: false }; + } + + // For any incoming property. + for (let prop in aValues) { + // If this is unknown for the app, let's continue. + if (!(prop in filtersMap)) { + continue; + } + + // Otherwise, let's check the value against the filter. + if (!matchObject(aValues[prop], filtersMap[prop].filter)) { + return false; + } + + filtersMap[prop].found = true; + } + + // Required filters: + for (let filter in filtersMap) { + if (filtersMap[filter].filter.required && !filtersMap[filter].found) { + return false; + } + } + + return true; + } +} diff --git a/dom/activities/src/Makefile.in b/dom/activities/src/Makefile.in index 83fdbfd63635..c61478fe932d 100644 --- a/dom/activities/src/Makefile.in +++ b/dom/activities/src/Makefile.in @@ -36,6 +36,7 @@ EXTRA_COMPONENTS = \ EXTRA_JS_MODULES = \ ActivitiesService.jsm \ + ActivitiesServiceFilter.jsm \ $(NULL) include $(topsrcdir)/config/config.mk diff --git a/dom/activities/tests/Makefile.in b/dom/activities/tests/Makefile.in new file mode 100644 index 000000000000..f095455bd1cc --- /dev/null +++ b/dom/activities/tests/Makefile.in @@ -0,0 +1,17 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = @relativesrcdir@ + +FAIL_ON_WARNINGS := 1 + +include $(DEPTH)/config/autoconf.mk + +XPCSHELL_TESTS = unit + +include $(topsrcdir)/config/rules.mk diff --git a/dom/activities/tests/unit/test_activityFilters.js b/dom/activities/tests/unit/test_activityFilters.js new file mode 100644 index 000000000000..0df15f0eb9d3 --- /dev/null +++ b/dom/activities/tests/unit/test_activityFilters.js @@ -0,0 +1,151 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function run_test() { + Components.utils.import("resource:///modules/ActivitiesServiceFilter.jsm") + + do_check_true(!!ActivitiesServiceFilter); + + // No requests, no filters: + do_check_true(ActivitiesServiceFilter.match(null, null)); + do_check_true(ActivitiesServiceFilter.match({}, {})); + + // No filters: + do_check_true(ActivitiesServiceFilter.match({foobar: 42}, null)); + + // Empty request: + do_check_true(ActivitiesServiceFilter.match({}, {a: 'foobar', b: [1, 2, 3], c: 42})); + + + // Simple match: + do_check_true(ActivitiesServiceFilter.match({a: 'foobar'}, + {a: 'foobar', b: [1, 2, 3], c: 42})); + do_check_false(ActivitiesServiceFilter.match({a: 'foobar', b: 2, c: true}, + {a: 'foobar', b: [1, 2, 3], c: 42})); + do_check_true(ActivitiesServiceFilter.match({a: 'foobar', b: 2, c: 42}, + {a: 'foobar', b: [1, 2, 3], c: 42})); + do_check_false(ActivitiesServiceFilter.match({a: 'foobar2'}, + {a: 'foobar', b: [1, 2, 3], c: 42})); + + // Simple match in array: + do_check_true(ActivitiesServiceFilter.match({b: 2}, + {a: 'foobar', b: [1, 2, 3], c: 42})); + do_check_false(ActivitiesServiceFilter.match({b: 4}, + {a: 'foobar', b: [1, 2, 3], c: 42})); + do_check_true(ActivitiesServiceFilter.match({b: [2, 4]}, + {a: 'foobar', b: [1, 2, 3], c: 42})); + do_check_false(ActivitiesServiceFilter.match({b: [4, 5]}, + {a: 'foobar', b: [1, 2, 3], c: 42})); + do_check_false(ActivitiesServiceFilter.match({a: [4, 'foobar2']}, + {a: 'foobar', b: [1, 2, 3], c: 42})); + do_check_true(ActivitiesServiceFilter.match({a: [4, 'foobar']}, + {a: 'foobar', b: [1, 2, 3], c: 42})); + do_check_true(ActivitiesServiceFilter.match({a: ['foo', 'bar']}, + {a: 'foo'})); + + // Unknown property + do_check_true(ActivitiesServiceFilter.match({k: 4}, + {a: 'foobar', b: [1, 2, 3], c: 42})); + do_check_true(ActivitiesServiceFilter.match({k: [1,2,3,4]}, + {a: 'foobar', b: [1, 2, 3], c: 42})); + + // Required/non required + do_check_false(ActivitiesServiceFilter.match({}, + {a: { required: true, value: 'foobar'}})); + do_check_true(ActivitiesServiceFilter.match({a: 'foobar'}, + {a: { required: true, value: 'foobar'}})); + do_check_false(ActivitiesServiceFilter.match({a: 'foobar2'}, + {a: { required: true, value: 'foobar'}})); + do_check_false(ActivitiesServiceFilter.match({a: 'foobar2'}, + {a: { required: true, value: ['a', 'b', 'foobar']}})); + do_check_true(ActivitiesServiceFilter.match({a: 'foobar'}, + {a: { required: true, value: ['a', 'b', 'foobar']}})); + do_check_true(ActivitiesServiceFilter.match({a: ['k', 'z', 'foobar']}, + {a: { required: true, value: ['a', 'b', 'foobar']}})); + do_check_false(ActivitiesServiceFilter.match({a: ['k', 'z', 'foobar2']}, + {a: { required: true, value: ['a', 'b', 'foobar']}})); + + // Empty values + do_check_true(ActivitiesServiceFilter.match({a: 42}, + {a: { required: true}})); + do_check_false(ActivitiesServiceFilter.match({}, + {a: { required: true}})); + + // Boolean + do_check_true(ActivitiesServiceFilter.match({a: false}, + {a: { required: true, value: false}})); + do_check_false(ActivitiesServiceFilter.match({a: true}, + {a: { required: true, value: false}})); + do_check_true(ActivitiesServiceFilter.match({a: [false, true]}, + {a: { required: true, value: false}})); + do_check_true(ActivitiesServiceFilter.match({a: [false, true]}, + {a: { required: true, value: [false,true]}})); + + // Number + do_check_true(ActivitiesServiceFilter.match({a: 42}, + {a: { required: true, value: 42}})); + do_check_false(ActivitiesServiceFilter.match({a: 2}, + {a: { required: true, value: 42}})); + do_check_true(ActivitiesServiceFilter.match({a: 2}, + {a: { required: true, min: 1}})); + do_check_true(ActivitiesServiceFilter.match({a: 2}, + {a: { required: true, min: 2}})); + do_check_false(ActivitiesServiceFilter.match({a: 2}, + {a: { required: true, min: 3}})); + do_check_false(ActivitiesServiceFilter.match({a: 2}, + {a: { required: true, max: 1}})); + do_check_true(ActivitiesServiceFilter.match({a: 2}, + {a: { required: true, max: 2}})); + do_check_true(ActivitiesServiceFilter.match({a: 2}, + {a: { required: true, max: 3}})); + do_check_false(ActivitiesServiceFilter.match({a: 2}, + {a: { required: true, min: 1, max: 1}})); + do_check_true(ActivitiesServiceFilter.match({a: 2}, + {a: { required: true, min: 1, max: 2}})); + do_check_true(ActivitiesServiceFilter.match({a: 2}, + {a: { required: true, min: 2, max: 2}})); + do_check_false(ActivitiesServiceFilter.match({a: 2}, + {a: { required: true, value: 'foo'}})); + do_check_false(ActivitiesServiceFilter.match({a: 2}, + {a: { required: true, min: 100, max: 0}})); + do_check_true(ActivitiesServiceFilter.match({a: 2}, + {a: { required: true, min: 'a', max: 'b'}})); + do_check_false(ActivitiesServiceFilter.match({a: 2}, + {a: { required: true, min: 10, max: 1}})); + + // String + do_check_true(ActivitiesServiceFilter.match({a: 'foo'}, + {a: { required: true, value: 'foo'}})); + do_check_false(ActivitiesServiceFilter.match({a: 'foo2'}, + {a: { required: true, value: 'foo'}})); + + // Number VS string + do_check_true(ActivitiesServiceFilter.match({a: '42'}, + {a: { required: true, value: 42}})); + do_check_true(ActivitiesServiceFilter.match({a: 42}, + {a: { required: true, value: '42'}})); + do_check_true(ActivitiesServiceFilter.match({a: '-42e+12'}, + {a: { required: true, value: -42e+12}})); + do_check_true(ActivitiesServiceFilter.match({a: 42}, + {a: { required: true, min: '1', max: '50'}})); + do_check_true(ActivitiesServiceFilter.match({a: '42'}, + {a: 42 })); + do_check_true(ActivitiesServiceFilter.match({a: 42}, + {a: '42' })); + do_check_false(ActivitiesServiceFilter.match({a: 42}, + {a: { min: '44' }})); + do_check_false(ActivitiesServiceFilter.match({a: 42}, + {a: { max: '0' }})); + + // String + RegExp + do_check_true(ActivitiesServiceFilter.match({a: 'foobar'}, + {a: { required: true, regexp: '/^foobar/'}})); + do_check_false(ActivitiesServiceFilter.match({a: 'aafoobar'}, + {a: { required: true, regexp: '/^foobar/'}})); + do_check_true(ActivitiesServiceFilter.match({a: 'aaFOOsdsad'}, + {a: { required: true, regexp: '/foo/i'}})); + do_check_true(ActivitiesServiceFilter.match({a: 'aafoobarasdsad'}, + {a: { required: true, regexp: '/foo/'}})); + do_check_false(ActivitiesServiceFilter.match({a: 'aaFOOsdsad'}, + {a: { required: true, regexp: '/foo/'}})); +} diff --git a/dom/activities/tests/unit/xpcshell.ini b/dom/activities/tests/unit/xpcshell.ini new file mode 100644 index 000000000000..d7aeee7af952 --- /dev/null +++ b/dom/activities/tests/unit/xpcshell.ini @@ -0,0 +1,5 @@ +[DEFAULT] +head = +tail = + +[test_activityFilters.js] diff --git a/testing/xpcshell/xpcshell.ini b/testing/xpcshell/xpcshell.ini index 701934d847e1..2010a763dfbb 100644 --- a/testing/xpcshell/xpcshell.ini +++ b/testing/xpcshell/xpcshell.ini @@ -9,6 +9,7 @@ [include:uriloader/exthandler/tests/unit/xpcshell.ini] [include:parser/xml/test/unit/xpcshell.ini] [include:image/test/unit/xpcshell.ini] +[include:dom/activities/tests/unit/xpcshell.ini] [include:dom/encoding/test/unit/xpcshell.ini] [include:dom/plugins/test/unit/xpcshell.ini] [include:dom/sms/tests/xpcshell.ini]