This commit is contained in:
Jared Wein 2012-05-03 15:10:54 -04:00
commit cc20c6669c
8 changed files with 374 additions and 325 deletions

View File

@ -498,7 +498,7 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo
}
@Override
protected boolean shouldIgnore(Record record) {
public boolean shouldIgnore(Record record) {
if (!(record instanceof BookmarkRecord)) {
return true;
}

View File

@ -70,6 +70,18 @@ public class AndroidBrowserHistoryRepositorySession extends AndroidBrowserReposi
return hist.histURI;
}
@Override
public boolean shouldIgnore(Record record) {
if (super.shouldIgnore(record)) {
return true;
}
if (!(record instanceof HistoryRecord)) {
return true;
}
HistoryRecord r = (HistoryRecord) record;
return !RepoUtils.isValidHistoryURI(r.histURI);
}
@Override
protected Record transformRecord(Record record) throws NullCursorException {
return addVisitsToRecord(record);
@ -242,4 +254,4 @@ public class AndroidBrowserHistoryRepositorySession extends AndroidBrowserReposi
}
});
}
}
}

View File

@ -91,7 +91,7 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos
*
* For example, a session subclass might skip records of an unsupported type.
*/
protected boolean shouldIgnore(Record record) {
public boolean shouldIgnore(Record record) {
return false;
}

View File

@ -119,6 +119,45 @@ public class RepoUtils {
}
}
/**
* Return true if the provided URI is non-empty and acceptable to Fennec
* (i.e., not an undesirable scheme).
*
* This code is pilfered from Fennec, which pilfered from Places.
*/
public static boolean isValidHistoryURI(String uri) {
if (uri == null || uri.length() == 0) {
return false;
}
// First, check the most common cases (HTTP, HTTPS) to avoid most of the work.
if (uri.startsWith("http:") || uri.startsWith("https:")) {
return true;
}
String scheme = Uri.parse(uri).getScheme();
if (scheme == null) {
return false;
}
// Now check for all bad things.
if (scheme.equals("about") ||
scheme.equals("imap") ||
scheme.equals("news") ||
scheme.equals("mailbox") ||
scheme.equals("moz-anno") ||
scheme.equals("view-source") ||
scheme.equals("chrome") ||
scheme.equals("resource") ||
scheme.equals("data") ||
scheme.equals("wyciwyg") ||
scheme.equals("javascript")) {
return false;
}
return true;
}
/**
* Create a HistoryRecord object from a cursor row.
*
@ -133,8 +172,8 @@ public class RepoUtils {
}
final String historyURI = getStringFromCursor(cur, BrowserContract.History.URL);
if (historyURI == null || (historyURI.length() == 0)) {
Logger.debug(LOG_TAG, "Skipping history record " + guid + " with null or empty URI.");
if (!isValidHistoryURI(historyURI)) {
Logger.debug(LOG_TAG, "Skipping history record " + guid + " with unwanted/invalid URI " + historyURI);
return null;
}

View File

@ -72,6 +72,9 @@ installer:
package:
@$(MAKE) -C mobile/android/installer
fast-package:
@$(MAKE) package MOZ_FAST_PACKAGE=1
ifeq ($(OS_TARGET),Android)
ifeq ($(MOZ_ANDROID_INSTALL_TARGET),)
# Determine if there's more than one device connected

View File

@ -71,7 +71,7 @@ package:
@$(MAKE) -C mobile/xul/installer
fast-package:
@MOZ_FAST_PACKAGE=1 $(MAKE) package
@$(MAKE) package MOZ_FAST_PACKAGE=1
install::
@echo "Mobile can't be installed directly."

View File

@ -173,9 +173,14 @@ class TypelibCompareMixin:
self.assertEqual(t1.size_is_arg_num, t2.size_is_arg_num)
self.assertEqual(t1.length_is_arg_num, t2.length_is_arg_num)
#TODO: test flags in various combinations
class TestTypelibRoundtrip(unittest.TestCase, TypelibCompareMixin):
def checkRoundtrip(self, t):
class TestTypelibReadWrite(unittest.TestCase, TypelibCompareMixin):
def test_read_file(self):
"""
Test that a Typelib can be read/written from/to a file.
"""
t = xpt.Typelib()
# add an unresolved interface
t.interfaces.append(xpt.Interface("IFoo"))
fd, f = tempfile.mkstemp()
os.close(fd)
t.write(f)
@ -183,6 +188,17 @@ class TestTypelibRoundtrip(unittest.TestCase, TypelibCompareMixin):
os.remove(f)
self.assert_(t2 is not None)
self.assertEqualTypelibs(t, t2)
#TODO: test flags in various combinations
class TestTypelibRoundtrip(unittest.TestCase, TypelibCompareMixin):
def checkRoundtrip(self, t):
s = StringIO()
t.write(s)
s.seek(0)
t2 = xpt.Typelib.read(s)
self.assert_(t2 is not None)
self.assertEqualTypelibs(t, t2)
def test_simple(self):
t = xpt.Typelib()
@ -362,7 +378,7 @@ class TestInterfaceCmp(unittest.TestCase):
methods=[m])
self.assert_(i2 == i1)
class TestTypelibMerge(unittest.TestCase):
class TestXPTLink(unittest.TestCase):
def test_mergeDifferent(self):
"""
Test that merging two typelibs with completely different interfaces
@ -375,12 +391,12 @@ class TestTypelibMerge(unittest.TestCase):
t2 = xpt.Typelib()
# add an unresolved interface
t2.interfaces.append(xpt.Interface("IBar"))
t1.merge(t2)
t3 = xpt.xpt_link([t1, t2])
self.assertEqual(2, len(t1.interfaces))
self.assertEqual(2, len(t3.interfaces))
# Interfaces should wind up sorted
self.assertEqual("IBar", t1.interfaces[0].name)
self.assertEqual("IFoo", t1.interfaces[1].name)
self.assertEqual("IBar", t3.interfaces[0].name)
self.assertEqual("IFoo", t3.interfaces[1].name)
# Add some IID values
t1 = xpt.Typelib()
@ -389,12 +405,12 @@ class TestTypelibMerge(unittest.TestCase):
t2 = xpt.Typelib()
# add an unresolved interface
t2.interfaces.append(xpt.Interface("IBar", iid="44332211-6655-8877-0099-aabbccddeeff"))
t1.merge(t2)
t3 = xpt.xpt_link([t1, t2])
self.assertEqual(2, len(t1.interfaces))
self.assertEqual(2, len(t3.interfaces))
# Interfaces should wind up sorted
self.assertEqual("IFoo", t1.interfaces[0].name)
self.assertEqual("IBar", t1.interfaces[1].name)
self.assertEqual("IFoo", t3.interfaces[0].name)
self.assertEqual("IBar", t3.interfaces[1].name)
def test_mergeConflict(self):
"""
@ -409,7 +425,7 @@ class TestTypelibMerge(unittest.TestCase):
t2 = xpt.Typelib()
# add an unresolved interface, same name different IID
t2.interfaces.append(xpt.Interface("IFoo", iid="44332211-6655-8877-0099-aabbccddeeff"))
self.assertRaises(xpt.DataError, t1.merge, t2)
self.assertRaises(xpt.DataError, xpt.xpt_link, [t1, t2])
# Same IIDs, different names
t1 = xpt.Typelib()
@ -418,7 +434,7 @@ class TestTypelibMerge(unittest.TestCase):
t2 = xpt.Typelib()
# add an unresolved interface, same IID different name
t2.interfaces.append(xpt.Interface("IBar", iid="11223344-5566-7788-9900-aabbccddeeff"))
self.assertRaises(xpt.DataError, t1.merge, t2)
self.assertRaises(xpt.DataError, xpt.xpt_link, [t1, t2])
def test_mergeUnresolvedIID(self):
"""
@ -434,11 +450,11 @@ class TestTypelibMerge(unittest.TestCase):
t2 = xpt.Typelib()
# add an unresolved interface, no IID
t2.interfaces.append(xpt.Interface("IFoo"))
t1.merge(t2)
t3 = xpt.xpt_link([t1, t2])
self.assertEqual(1, len(t1.interfaces))
self.assertEqual("IFoo", t1.interfaces[0].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t1.interfaces[0].iid)
self.assertEqual(1, len(t3.interfaces))
self.assertEqual("IFoo", t3.interfaces[0].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[0].iid)
# Unresolved in both, but t2 has an IID value
t1 = xpt.Typelib()
# add an unresolved interface, no IID
@ -446,11 +462,11 @@ class TestTypelibMerge(unittest.TestCase):
t2 = xpt.Typelib()
# add an unresolved interface with a valid IID
t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff"))
t1.merge(t2)
t3 = xpt.xpt_link([t1, t2])
self.assertEqual(1, len(t1.interfaces))
self.assertEqual("IFoo", t1.interfaces[0].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t1.interfaces[0].iid)
self.assertEqual(1, len(t3.interfaces))
self.assertEqual("IFoo", t3.interfaces[0].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[0].iid)
def test_mergeResolvedUnresolved(self):
"""
@ -470,14 +486,14 @@ class TestTypelibMerge(unittest.TestCase):
m = xpt.Method("Bar", p)
t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
methods=[m]))
t1.merge(t2)
t3 = xpt.xpt_link([t1, t2])
self.assertEqual(1, len(t1.interfaces))
self.assertEqual("IFoo", t1.interfaces[0].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t1.interfaces[0].iid)
self.assert_(t1.interfaces[0].resolved)
self.assertEqual(1, len(t1.interfaces[0].methods))
self.assertEqual("Bar", t1.interfaces[0].methods[0].name)
self.assertEqual(1, len(t3.interfaces))
self.assertEqual("IFoo", t3.interfaces[0].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[0].iid)
self.assert_(t3.interfaces[0].resolved)
self.assertEqual(1, len(t3.interfaces[0].methods))
self.assertEqual("Bar", t3.interfaces[0].methods[0].name)
# t1 has a resolved interface, t2 has an unresolved version
t1 = xpt.Typelib()
@ -489,14 +505,14 @@ class TestTypelibMerge(unittest.TestCase):
t2 = xpt.Typelib()
# add an unresolved interface
t2.interfaces.append(xpt.Interface("IFoo"))
t1.merge(t2)
t3 = xpt.xpt_link([t1, t2])
self.assertEqual(1, len(t1.interfaces))
self.assertEqual("IFoo", t1.interfaces[0].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t1.interfaces[0].iid)
self.assert_(t1.interfaces[0].resolved)
self.assertEqual(1, len(t1.interfaces[0].methods))
self.assertEqual("Bar", t1.interfaces[0].methods[0].name)
self.assertEqual(1, len(t3.interfaces))
self.assertEqual("IFoo", t3.interfaces[0].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[0].iid)
self.assert_(t3.interfaces[0].resolved)
self.assertEqual(1, len(t3.interfaces[0].methods))
self.assertEqual("Bar", t3.interfaces[0].methods[0].name)
def test_mergeReplaceParents(self):
"""
@ -520,17 +536,17 @@ class TestTypelibMerge(unittest.TestCase):
m = xpt.Method("Bar", p)
t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
methods=[m]))
t1.merge(t2)
t3 = xpt.xpt_link([t1, t2])
self.assertEqual(2, len(t1.interfaces))
self.assertEqual("IChild", t1.interfaces[0].name)
self.assertEqual("11111111-1111-1111-1111-111111111111", t1.interfaces[0].iid)
self.assertEqual("IFoo", t1.interfaces[1].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t1.interfaces[1].iid)
self.assert_(t1.interfaces[0].resolved)
self.assertEqual(2, len(t3.interfaces))
self.assertEqual("IChild", t3.interfaces[0].name)
self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
self.assertEqual("IFoo", t3.interfaces[1].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
self.assert_(t3.interfaces[0].resolved)
# Ensure that IChild's parent has been updated
self.assertEqual(t1.interfaces[1], t1.interfaces[0].parent)
self.assert_(t1.interfaces[0].parent.resolved)
self.assertEqual(t3.interfaces[1], t3.interfaces[0].parent)
self.assert_(t3.interfaces[0].parent.resolved)
# t1 has a resolved interface, t2 has an unresolved version,
# but t2 also has another interface whose parent is the unresolved
@ -548,17 +564,17 @@ class TestTypelibMerge(unittest.TestCase):
# add a child of the unresolved interface
t2.interfaces.append(xpt.Interface("IChild", iid="11111111-1111-1111-1111-111111111111",
resolved=True, parent=pi))
t1.merge(t2)
t3 = xpt.xpt_link([t1, t2])
self.assertEqual(2, len(t1.interfaces))
self.assertEqual("IChild", t1.interfaces[0].name)
self.assertEqual("11111111-1111-1111-1111-111111111111", t1.interfaces[0].iid)
self.assertEqual("IFoo", t1.interfaces[1].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t1.interfaces[1].iid)
self.assert_(t1.interfaces[0].resolved)
self.assertEqual(2, len(t3.interfaces))
self.assertEqual("IChild", t3.interfaces[0].name)
self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
self.assertEqual("IFoo", t3.interfaces[1].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
self.assert_(t3.interfaces[0].resolved)
# Ensure that IChild's parent has been updated
self.assertEqual(t1.interfaces[1], t1.interfaces[0].parent)
self.assert_(t1.interfaces[0].parent.resolved)
self.assertEqual(t3.interfaces[1], t3.interfaces[0].parent)
self.assert_(t3.interfaces[0].parent.resolved)
def test_mergeReplaceRetval(self):
"""
@ -585,19 +601,19 @@ class TestTypelibMerge(unittest.TestCase):
m = xpt.Method("Bar", p)
t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
methods=[m]))
t1.merge(t2)
t3 = xpt.xpt_link([t1, t2])
self.assertEqual(2, len(t1.interfaces))
self.assertEqual("IRetval", t1.interfaces[0].name)
self.assertEqual("11111111-1111-1111-1111-111111111111", t1.interfaces[0].iid)
self.assertEqual("IFoo", t1.interfaces[1].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t1.interfaces[1].iid)
self.assert_(t1.interfaces[1].resolved)
self.assertEqual(2, len(t3.interfaces))
self.assertEqual("IRetval", t3.interfaces[0].name)
self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
self.assertEqual("IFoo", t3.interfaces[1].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
self.assert_(t3.interfaces[1].resolved)
# Ensure that IRetval's method's return value type has been updated.
self.assertEqual(1, len(t1.interfaces[0].methods))
self.assert_(t1.interfaces[0].methods[0].result.type.iface.resolved)
self.assertEqual(t1.interfaces[1],
t1.interfaces[0].methods[0].result.type.iface)
self.assertEqual(1, len(t3.interfaces[0].methods))
self.assert_(t3.interfaces[0].methods[0].result.type.iface.resolved)
self.assertEqual(t3.interfaces[1],
t3.interfaces[0].methods[0].result.type.iface)
# t1 has a resolved interface. t2 has an unresolved version and
# an interface that uses the unresolved interface as a return value
@ -618,19 +634,19 @@ class TestTypelibMerge(unittest.TestCase):
m = xpt.Method("ReturnIface", p)
t2.interfaces.append(xpt.Interface("IRetval", iid="11111111-1111-1111-1111-111111111111",
methods=[m]))
t1.merge(t2)
t3 = xpt.xpt_link([t1, t2])
self.assertEqual(2, len(t1.interfaces))
self.assertEqual("IRetval", t1.interfaces[0].name)
self.assertEqual("11111111-1111-1111-1111-111111111111", t1.interfaces[0].iid)
self.assertEqual("IFoo", t1.interfaces[1].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t1.interfaces[1].iid)
self.assert_(t1.interfaces[1].resolved)
self.assertEqual(2, len(t3.interfaces))
self.assertEqual("IRetval", t3.interfaces[0].name)
self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
self.assertEqual("IFoo", t3.interfaces[1].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
self.assert_(t3.interfaces[1].resolved)
# Ensure that IRetval's method's return value type has been updated.
self.assertEqual(1, len(t1.interfaces[0].methods))
self.assert_(t1.interfaces[0].methods[0].result.type.iface.resolved)
self.assertEqual(t1.interfaces[1],
t1.interfaces[0].methods[0].result.type.iface)
self.assertEqual(1, len(t3.interfaces[0].methods))
self.assert_(t3.interfaces[0].methods[0].result.type.iface.resolved)
self.assertEqual(t3.interfaces[1],
t3.interfaces[0].methods[0].result.type.iface)
def test_mergeReplaceParams(self):
"""
@ -657,19 +673,19 @@ class TestTypelibMerge(unittest.TestCase):
m = xpt.Method("Bar", vp)
t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
methods=[m]))
t1.merge(t2)
t3 = xpt.xpt_link([t1, t2])
self.assertEqual(2, len(t1.interfaces))
self.assertEqual("IParam", t1.interfaces[0].name)
self.assertEqual("11111111-1111-1111-1111-111111111111", t1.interfaces[0].iid)
self.assertEqual("IFoo", t1.interfaces[1].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t1.interfaces[1].iid)
self.assert_(t1.interfaces[1].resolved)
self.assertEqual(2, len(t3.interfaces))
self.assertEqual("IParam", t3.interfaces[0].name)
self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
self.assertEqual("IFoo", t3.interfaces[1].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
self.assert_(t3.interfaces[1].resolved)
# Ensure that IRetval's method's param type has been updated.
self.assertEqual(1, len(t1.interfaces[0].methods))
self.assert_(t1.interfaces[0].methods[0].params[0].type.iface.resolved)
self.assertEqual(t1.interfaces[1],
t1.interfaces[0].methods[0].params[0].type.iface)
self.assertEqual(1, len(t3.interfaces[0].methods))
self.assert_(t3.interfaces[0].methods[0].params[0].type.iface.resolved)
self.assertEqual(t3.interfaces[1],
t3.interfaces[0].methods[0].params[0].type.iface)
# t1 has a resolved interface. t2 has an unresolved version
# and an interface that uses the unresolved interface as a
@ -690,19 +706,19 @@ class TestTypelibMerge(unittest.TestCase):
m = xpt.Method("IfaceParam", vp, params=[p])
t2.interfaces.append(xpt.Interface("IParam", iid="11111111-1111-1111-1111-111111111111",
methods=[m]))
t1.merge(t2)
t3 = xpt.xpt_link([t1, t2])
self.assertEqual(2, len(t1.interfaces))
self.assertEqual("IParam", t1.interfaces[0].name)
self.assertEqual("11111111-1111-1111-1111-111111111111", t1.interfaces[0].iid)
self.assertEqual("IFoo", t1.interfaces[1].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t1.interfaces[1].iid)
self.assert_(t1.interfaces[1].resolved)
self.assertEqual(2, len(t3.interfaces))
self.assertEqual("IParam", t3.interfaces[0].name)
self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
self.assertEqual("IFoo", t3.interfaces[1].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
self.assert_(t3.interfaces[1].resolved)
# Ensure that IRetval's method's param type has been updated.
self.assertEqual(1, len(t1.interfaces[0].methods))
self.assert_(t1.interfaces[0].methods[0].params[0].type.iface.resolved)
self.assertEqual(t1.interfaces[1],
t1.interfaces[0].methods[0].params[0].type.iface)
self.assertEqual(1, len(t3.interfaces[0].methods))
self.assert_(t3.interfaces[0].methods[0].params[0].type.iface.resolved)
self.assertEqual(t3.interfaces[1],
t3.interfaces[0].methods[0].params[0].type.iface)
def test_mergeReplaceArrayTypeParams(self):
@ -732,79 +748,19 @@ class TestTypelibMerge(unittest.TestCase):
m = xpt.Method("Bar", vp)
t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
methods=[m]))
t1.merge(t2)
self.assertEqual(2, len(t1.interfaces))
self.assertEqual("IParam", t1.interfaces[0].name)
self.assertEqual("11111111-1111-1111-1111-111111111111", t1.interfaces[0].iid)
self.assertEqual("IFoo", t1.interfaces[1].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t1.interfaces[1].iid)
self.assert_(t1.interfaces[1].resolved)
# Ensure that IRetval's method's param type has been updated.
self.assertEqual(1, len(t1.interfaces[0].methods))
self.assert_(t1.interfaces[0].methods[0].params[0].type.element_type.iface.resolved)
self.assertEqual(t1.interfaces[1],
t1.interfaces[0].methods[0].params[0].type.element_type.iface)
class TestXPTLink(unittest.TestCase):
def setUp(self):
self.tempdir = tempfile.mkdtemp()
def tearDown(self):
shutil.rmtree(self.tempdir, True)
def gettempfile(self):
fd, f = tempfile.mkstemp(dir=self.tempdir)
os.close(fd)
return f
def test_xpt_link(self):
"""
Test the xpt_link method.
"""
t1 = xpt.Typelib()
# add an unresolved interface
t1.interfaces.append(xpt.Interface("IFoo"))
f1 = self.gettempfile()
t1.write(f1)
t2 = xpt.Typelib()
# add an unresolved interface
t2.interfaces.append(xpt.Interface("IBar"))
f2 = self.gettempfile()
t2.write(f2)
f3 = self.gettempfile()
xpt.xpt_link(f3, [f1, f2])
t3 = xpt.Typelib.read(f3)
t3 = xpt.xpt_link([t1, t2])
self.assertEqual(2, len(t3.interfaces))
# Interfaces should wind up sorted
self.assertEqual("IBar", t3.interfaces[0].name)
self.assertEqual("IParam", t3.interfaces[0].name)
self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
self.assertEqual("IFoo", t3.interfaces[1].name)
# Add some IID values
t1 = xpt.Typelib()
# add an unresolved interface
t1.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff"))
f1 = self.gettempfile()
t1.write(f1)
t2 = xpt.Typelib()
# add an unresolved interface
t2.interfaces.append(xpt.Interface("IBar", iid="44332211-6655-8877-0099-aabbccddeeff"))
f2 = self.gettempfile()
t2.write(f2)
f3 = self.gettempfile()
xpt.xpt_link(f3, [f1, f2])
t3 = xpt.Typelib.read(f3)
self.assertEqual(2, len(t3.interfaces))
# Interfaces should wind up sorted
self.assertEqual("IFoo", t3.interfaces[0].name)
self.assertEqual("IBar", t3.interfaces[1].name)
self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
self.assert_(t3.interfaces[1].resolved)
# Ensure that IRetval's method's param type has been updated.
self.assertEqual(1, len(t3.interfaces[0].methods))
self.assert_(t3.interfaces[0].methods[0].params[0].type.element_type.iface.resolved)
self.assertEqual(t3.interfaces[1],
t3.interfaces[0].methods[0].params[0].type.element_type.iface)
if __name__ == '__main__':
unittest.main()

View File

@ -43,8 +43,8 @@ a typelib in a high-level manner, as well as methods for reading
and writing them from files.
The usable public interfaces are currently:
Typelib.read(filename) - read a typelib from a file on disk, return
a Typelib object.
Typelib.read(input_file) - read a typelib from a file on disk or file-like
object, return a Typelib object.
xpt_dump(filename) - read a typelib from a file on disk, dump
the contents to stdout in a human-readable
@ -68,6 +68,7 @@ InterfaceType() - construct a new object representing a type that
from __future__ import with_statement
import os, sys
import struct
import operator
# header magic
XPT_MAGIC = "XPCOM\nTypeLib\r\n\x1a"
@ -878,6 +879,9 @@ class Interface(object):
def __str__(self):
return "Interface(name='%s', iid='%s')" % (self.name, self.iid)
def __hash__(self):
return hash((self.name, self.iid))
def __cmp__(self, other):
c = cmp(self.iid, other.iid)
if c != 0:
@ -1051,55 +1055,68 @@ class Typelib(object):
return map[data_pool + offset - 1:sz]
@staticmethod
def read(filename):
def read(input_file):
"""
Read a typelib from the file named |filename| and return
the constructed Typelib object.
Read a typelib from |input_file| and return
the constructed Typelib object. |input_file| can be a filename
or a file-like object.
"""
with open(filename, "r+b") as f:
st = os.fstat(f.fileno())
map = f.read(st.st_size)
data = Typelib._header.unpack(map[:Typelib._header.size])
if data[0] != XPT_MAGIC:
raise FileFormatError, "Bad magic: %s" % data[0]
xpt = Typelib((data[1], data[2]))
xpt.filename = filename
num_interfaces = data[3]
file_length = data[4]
if file_length != st.st_size:
raise FileFormatError, "File is of wrong length, got %d bytes, expected %d" % (st.st_size, file_length)
#XXX: by spec this is a zero-based file offset. however,
# the xpt_xdr code always subtracts 1 from data offsets
# (because that's what you do in the data pool) so it
# winds up accidentally treating this as 1-based.
# Filed as: https://bugzilla.mozilla.org/show_bug.cgi?id=575343
interface_directory_offset = data[5] - 1
data_pool_offset = data[6]
# make a half-hearted attempt to read Annotations,
# since XPIDL doesn't produce any anyway.
start = Typelib._header.size
(anno, ) = struct.unpack(">B", map[start:start + struct.calcsize(">B")])
islast = anno & 0x80
tag = anno & 0x7F
if tag == 0: # EmptyAnnotation
xpt.annotations.append(None)
# We don't bother handling PrivateAnnotations or anything
for i in range(num_interfaces):
# iid, name, namespace, interface_descriptor
start = interface_directory_offset + i * Interface._direntry.size
end = interface_directory_offset + (i+1) * Interface._direntry.size
ide = Interface._direntry.unpack(map[start:end])
iid = Typelib.iid_to_string(ide[0])
name = Typelib.read_string(map, data_pool_offset, ide[1])
namespace = Typelib.read_string(map, data_pool_offset, ide[2])
iface = Interface(name, iid, namespace)
iface._descriptor_offset = ide[3]
iface.xpt_filename = xpt.filename
xpt.interfaces.append(iface)
for iface in xpt.interfaces:
iface.read_descriptor(xpt, map, data_pool_offset)
filename = ""
data = None
expected_size = None
if isinstance(input_file, basestring):
filename = input_file
with open(input_file, "r+b") as f:
st = os.fstat(f.fileno())
data = f.read(st.st_size)
expected_size = st.st_size
else:
data = input_file.read()
(magic,
major_ver,
minor_ver,
num_interfaces,
file_length,
interface_directory_offset,
data_pool_offset) = Typelib._header.unpack(data[:Typelib._header.size])
if magic != XPT_MAGIC:
raise FileFormatError, "Bad magic: %s" % magic
xpt = Typelib((major_ver, minor_ver))
xpt.filename = filename
if expected_size and file_length != expected_size:
raise FileFormatError, "File is of wrong length, got %d bytes, expected %d" % (expected_size, file_length)
#XXX: by spec this is a zero-based file offset. however,
# the xpt_xdr code always subtracts 1 from data offsets
# (because that's what you do in the data pool) so it
# winds up accidentally treating this as 1-based.
# Filed as: https://bugzilla.mozilla.org/show_bug.cgi?id=575343
interface_directory_offset -= 1
# make a half-hearted attempt to read Annotations,
# since XPIDL doesn't produce any anyway.
start = Typelib._header.size
(anno, ) = struct.unpack(">B", data[start:start + struct.calcsize(">B")])
islast = anno & 0x80
tag = anno & 0x7F
if tag == 0: # EmptyAnnotation
xpt.annotations.append(None)
# We don't bother handling PrivateAnnotations or anything
for i in range(num_interfaces):
# iid, name, namespace, interface_descriptor
start = interface_directory_offset + i * Interface._direntry.size
end = interface_directory_offset + (i+1) * Interface._direntry.size
ide = Interface._direntry.unpack(data[start:end])
iid = Typelib.iid_to_string(ide[0])
name = Typelib.read_string(data, data_pool_offset, ide[1])
namespace = Typelib.read_string(data, data_pool_offset, ide[2])
iface = Interface(name, iid, namespace)
iface._descriptor_offset = ide[3]
iface.xpt_filename = xpt.filename
xpt.interfaces.append(iface)
for iface in xpt.interfaces:
iface.read_descriptor(xpt, data, data_pool_offset)
return xpt
def __repr__(self):
@ -1160,102 +1177,18 @@ class Typelib(object):
for i in self.interfaces:
i.write_directory_entry(fd)
def write(self, filename):
def write(self, output_file):
"""
Write the contents of this typelib to the file named |filename|.
Write the contents of this typelib to |output_file|,
which can be either a filename or a file-like object.
"""
self._sanityCheck()
with open(filename, "wb") as f:
self.writefd(f)
def merge(self, other, sanitycheck=True):
"""
Merge the contents of Typelib |other| into this typelib.
If |sanitycheck| is False, don't sort the interface table
after merging.
"""
# This will be a list of (replaced interface, replaced with)
# containing interfaces that were replaced with interfaces from
# another typelib, and the interface that replaced them.
merged_interfaces = []
for i in other.interfaces:
if i in self.interfaces:
continue
# See if there's a copy of this interface with different
# resolved status or IID value.
merged = False
for j in self.interfaces:
if i.name == j.name:
if i.resolved != j.resolved:
# prefer resolved interfaces over unresolved
if j.resolved:
# keep j
merged_interfaces.append((i, j))
merged = True
# Fixup will happen after processing all interfaces.
else:
# replace j with i
merged_interfaces.append((j, i))
merged = True
self.interfaces[self.interfaces.index(j)] = i
elif i.iid != j.iid:
# Prefer unresolved interfaces with valid IIDs
if j.iid == Interface.UNRESOLVED_IID:
# replace j with i
merged_interfaces.append((j, i))
merged = True
self.interfaces[self.interfaces.index(j)] = i
elif i.iid == Interface.UNRESOLVED_IID:
# keep j
merged_interfaces.append((i, j))
merged = True
# Fixup will happen after processing all interfaces.
else:
# Same name but different IIDs: raise an exception.
# self.* is the (target) Typelib being merged into,
# not the one which j.iid was from.
raise DataError, \
"Typelibs contain definitions of interface %s" \
" with different IIDs (%s (%s) vs %s (%s))!" % \
(i.name, i.iid, i.xpt_filename or other.filename, \
j.iid, j.xpt_filename or self.filename)
elif i.iid == j.iid and i.iid != Interface.UNRESOLVED_IID:
# Same IID but different names: raise an exception.
# self.* is the (target) Typelib being merged into,
# not the one which j.name was from.
raise DataError, \
"Typelibs contain definitions of interface %s" \
" with different names (%s (%s) vs %s (%s))!" % \
(i.iid, i.name, i.xpt_filename or other.filename, \
j.name, j.xpt_filename or self.filename)
if not merged:
# No partially matching interfaces, so just take this interface
self.interfaces.append(i)
# Now fixup any merged interfaces
def checkType(t, replaced_from, replaced_to):
if isinstance(t, InterfaceType) and t.iface == replaced_from:
t.iface = replaced_to
elif isinstance(t, ArrayType) and \
isinstance(t.element_type, InterfaceType) and \
t.element_type.iface == replaced_from:
t.element_type.iface = replaced_to
for replaced_from, replaced_to in merged_interfaces:
for i in self.interfaces:
# Replace parent references
if i.parent is not None and i.parent == replaced_from:
i.parent = replaced_to
for m in i.methods:
# Replace InterfaceType params and return values
checkType(m.result.type, replaced_from, replaced_to)
for p in m.params:
checkType(p.type, replaced_from, replaced_to)
if sanitycheck:
self._sanityCheck()
#TODO: do we care about annotations? probably not
if isinstance(output_file, basestring):
with open(output_file, "wb") as f:
self.writefd(f)
else:
self.writefd(output_file)
def dump(self, out):
"""
@ -1318,21 +1251,126 @@ def xpt_dump(file):
t = Typelib.read(file)
t.dump(sys.stdout)
def xpt_link(dest, inputs):
def xpt_link(inputs):
"""
Link all of the xpt files in |inputs| together and write the
result ot |dest|.
Link all of the xpt files in |inputs| together and return the result
as a Typelib object. All entries in inputs may be filenames or
file-like objects.
"""
def read_input(i):
if isinstance(i, Typelib):
return i
return Typelib.read(i)
if not inputs:
print >>sys.stderr, "Usage: xpt_link <destination file> <input files>"
return
t1 = Typelib.read(inputs[0])
for f in inputs[1:]:
t2 = Typelib.read(f)
# write will call sanitycheck, so skip it here.
t1.merge(t2, sanitycheck=False)
t1.write(dest)
return None
# This is the aggregate list of interfaces.
interfaces = []
# This will be a dict of replaced interface -> replaced with
# containing interfaces that were replaced with interfaces from
# another typelib, and the interface that replaced them.
merged_interfaces = {}
for f in inputs:
t = read_input(f)
interfaces.extend(t.interfaces)
# Sort interfaces by name so we can merge adjacent duplicates
interfaces.sort(key=operator.attrgetter('name'))
Result = enum('Equal', # Interfaces the same, doesn't matter
'NotEqual', # Interfaces differ, keep both
'KeepFirst', # Replace second interface with first
'KeepSecond')# Replace first interface with second
def compare(i, j):
"""
Compare two interfaces, determine if they're equal or
completely different, or should be merged (and indicate which
one to keep in that case).
"""
if i == j:
# Arbitrary, just pick one
return Result.Equal
if i.name != j.name:
if i.iid == j.iid and i.iid != Interface.UNRESOLVED_IID:
# Same IID but different names: raise an exception.
raise DataError, \
"Typelibs contain definitions of interface %s" \
" with different names (%s (%s) vs %s (%s))!" % \
(i.iid, i.name, i.xpt_filename, j.name, j.xpt_filename)
# Otherwise just different interfaces.
return Result.NotEqual
# Interfaces have the same name, so either they need to be merged
# or there's a data error. Sort out which one to keep
if i.resolved != j.resolved:
# prefer resolved interfaces over unresolved
if j.resolved:
assert i.iid == j.iid or i.iid == Interface.UNRESOLVED_IID
# keep j
return Result.KeepSecond
else:
assert i.iid == j.iid or j.iid == Interface.UNRESOLVED_IID
# replace j with i
return Result.KeepFirst
elif i.iid != j.iid:
# Prefer unresolved interfaces with valid IIDs
if j.iid == Interface.UNRESOLVED_IID:
# replace j with i
assert not j.resolved
return Result.KeepFirst
elif i.iid == Interface.UNRESOLVED_IID:
# keep j
assert not i.resolved
return Result.KeepSecond
else:
# Same name but different IIDs: raise an exception.
raise DataError, \
"Typelibs contain definitions of interface %s" \
" with different IIDs (%s (%s) vs %s (%s))!" % \
(i.name, i.iid, i.xpt_filename, \
j.iid, j.xpt_filename)
raise DataError, "No idea what happened here: %s:%s (%s), %s:%s (%s)" % \
(i.name, i.iid, i.xpt_filename, j.name, j.iid, j.xpt_filename)
# Compare interfaces pairwise to find duplicates that should be merged.
i = 1
while i < len(interfaces):
res = compare(interfaces[i-1], interfaces[i])
if res == Result.NotEqual:
i += 1
elif res == Result.Equal:
# Need to drop one but it doesn't matter which
del interfaces[i]
elif res == Result.KeepFirst:
merged_interfaces[interfaces[i]] = interfaces[i-1]
del interfaces[i]
elif res == Result.KeepSecond:
merged_interfaces[interfaces[i-1]] = interfaces[i]
del interfaces[i-1]
# Now fixup any merged interfaces
def checkType(t):
if isinstance(t, InterfaceType) and t.iface in merged_interfaces:
t.iface = merged_interfaces[t.iface]
elif isinstance(t, ArrayType) and \
isinstance(t.element_type, InterfaceType) and \
t.element_type.iface in merged_interfaces:
t.element_type.iface = merged_interfaces[t.element_type.iface]
for i in interfaces:
# Replace parent references
if i.parent in merged_interfaces:
i.parent = merged_interfaces[i.parent]
for m in i.methods:
# Replace InterfaceType params and return values
checkType(m.result.type)
for p in m.params:
checkType(p.type)
# Re-sort interfaces (by IID)
interfaces.sort()
return Typelib(interfaces=interfaces)
if __name__ == '__main__':
if len(sys.argv) < 3:
@ -1341,4 +1379,5 @@ if __name__ == '__main__':
if sys.argv[1] == 'dump':
xpt_dump(sys.argv[2])
elif sys.argv[1] == 'link':
xpt_link(sys.argv[2], sys.argv[3:])
xpt_link(sys.argv[3:]).write(sys.argv[2])