UNPKG

react-native-photos-framework

Version:

Use Apples Photos Framework with react-native to fetch media from CameraRoll and iCloud

476 lines (449 loc) 16.7 kB
/** * Copyright (c) 2016-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ 'use strict'; /*eslint no-console-disallow: "off"*/ /*global React ReactDOM Table stringInterner stackRegistry aggrow preLoadedCapture:true*/ function RefVisitor(refs, id) { this.refs = refs; this.id = id; } RefVisitor.prototype = { moveToEdge: function moveToEdge(name) { const ref = this.refs[this.id]; if (ref && ref.edges) { const edges = ref.edges; for (const edgeId in edges) { if (edges[edgeId] === name) { this.id = edgeId; return this; } } } this.id = undefined; return this; }, moveToFirst: function moveToFirst(callback) { const ref = this.refs[this.id]; if (ref && ref.edges) { const edges = ref.edges; for (const edgeId in edges) { this.id = edgeId; if (callback(edges[edgeId], this)) { return this; } } } this.id = undefined; return this; }, forEachEdge: function forEachEdge(callback) { const ref = this.refs[this.id]; if (ref && ref.edges) { const edges = ref.edges; const visitor = new RefVisitor(this.refs, undefined); for (const edgeId in edges) { visitor.id = edgeId; callback(edges[edgeId], visitor); } } }, getType: function getType() { const ref = this.refs[this.id]; if (ref) { return ref.type; } return undefined; }, getRef: function getRef() { return this.refs[this.id]; }, clone: function clone() { return new RefVisitor(this.refs, this.id); }, isDefined: function isDefined() { return !!this.id; }, getValue: function getValue() { const ref = this.refs[this.id]; if (ref) { if (ref.type === 'string') { if (ref.value) { return ref.value; } else { const rope = []; this.forEachEdge((name, visitor) => { if (name && name.startsWith('[') && name.endsWith(']')) { const index = parseInt(name.substring(1, name.length - 1), 10); rope[index] = visitor.getValue(); } }); return rope.join(''); } } else if (ref.type === 'ScriptExecutable' || ref.type === 'EvalExecutable' || ref.type === 'ProgramExecutable') { return ref.value.url + ':' + ref.value.line + ':' + ref.value.col; } else if (ref.type === 'FunctionExecutable') { return ref.value.name + '@' + ref.value.url + ':' + ref.value.line + ':' + ref.value.col; } else if (ref.type === 'NativeExecutable') { return ref.value.function + ' ' + ref.value.constructor + ' ' + ref.value.name; } else if (ref.type === 'Function') { const executable = this.clone().moveToEdge('@Executable'); if (executable.id) { return executable.getRef().type + ' ' + executable.getValue(); } } } return '#none'; } }; function forEachRef(refs, callback) { const visitor = new RefVisitor(refs, undefined); for (const id in refs) { visitor.id = id; callback(visitor); } } function firstRef(refs, callback) { for (const id in refs) { const ref = refs[id]; if (callback(id, ref)) { return new RefVisitor(refs, id); } } return new RefVisitor(refs, undefined); } function getInternalInstanceName(visitor) { const type = visitor.clone().moveToEdge('_currentElement').moveToEdge('type'); if (type.getType() === 'string') { // element.type is string return type.getValue(); } else if (type.getType() === 'Function') { // element.type is function const displayName = type.clone().moveToEdge('displayName'); if (displayName.isDefined()) { return displayName.getValue(); // element.type.displayName } const name = type.clone().moveToEdge('name'); if (name.isDefined()) { return name.getValue(); // element.type.name } type.moveToEdge('@Executable'); if (type.getType() === 'FunctionExecutable') { return type.getRef().value.name; // element.type symbolicated name } } return '#unknown'; } function buildReactComponentTree(visitor, registry) { const ref = visitor.getRef(); if (ref.reactTree || ref.reactParent === undefined) { return; // has one or doesn't need one } const parentVisitor = ref.reactParent; if (parentVisitor === null) { ref.reactTree = registry.insert(registry.root, getInternalInstanceName(visitor)); } else if (parentVisitor) { const parentRef = parentVisitor.getRef(); buildReactComponentTree(parentVisitor, registry); let relativeName = getInternalInstanceName(visitor); if (ref.reactKey) { relativeName = ref.reactKey + ': ' + relativeName; } ref.reactTree = registry.insert(parentRef.reactTree, relativeName); } else { throw 'non react instance parent of react instance'; } } function markReactComponentTree(refs, registry) { // annotate all refs that are react internal instances with their parent and name // ref.reactParent = visitor that points to parent instance, // null if we know it's an instance, but don't have a parent yet // ref.reactKey = if a key is used to distinguish siblings forEachRef(refs, (visitor) => { const visitorClone = visitor.clone(); // visitor will get stomped on next iteration const ref = visitor.getRef(); visitor.forEachEdge((edgeName, edgeVisitor) => { const edgeRef = edgeVisitor.getRef(); if (edgeRef) { if (edgeName === '_renderedChildren') { if (ref.reactParent === undefined) { // ref is react component, even if we don't have a parent yet ref.reactParent = null; } edgeVisitor.forEachEdge((childName, childVisitor) => { const childRef = childVisitor.getRef(); if (childRef && childName.startsWith('.')) { childRef.reactParent = visitorClone; childRef.reactKey = childName; } }); } else if (edgeName === '_renderedComponent') { if (ref.reactParent === undefined) { ref.reactParent = null; } edgeRef.reactParent = visitorClone; } } }); }); // build tree of react internal instances (since that's what has the structure) // fill in ref.reactTree = path registry node forEachRef(refs, (visitor) => { buildReactComponentTree(visitor, registry); }); // hook in components by looking at their _reactInternalInstance fields forEachRef(refs, (visitor) => { const ref = visitor.getRef(); const instanceRef = visitor.moveToEdge('_reactInternalInstance').getRef(); if (instanceRef) { ref.reactTree = instanceRef.reactTree; } }); } function functionUrlFileName(visitor) { const executable = visitor.clone().moveToEdge('@Executable'); const ref = executable.getRef(); if (ref && ref.value && ref.value.url) { const url = ref.value.url; let file = url.substring(url.lastIndexOf('/') + 1); if (file.endsWith('.js')) { file = file.substring(0, file.length - 3); } return file; } return undefined; } function markModules(refs) { const modules = firstRef(refs, (id, ref) => ref.type === 'CallbackGlobalObject'); modules.moveToEdge('require'); modules.moveToFirst((name, visitor) => visitor.getType() === 'JSActivation'); modules.moveToEdge('modules'); modules.forEachEdge((name, visitor) => { const ref = visitor.getRef(); visitor.moveToEdge('exports'); if (visitor.getType() === 'Object') { visitor.moveToFirst((memberName, member) => member.getType() === 'Function'); if (visitor.isDefined()) { ref.module = functionUrlFileName(visitor); } } else if (visitor.getType() === 'Function') { const displayName = visitor.clone().moveToEdge('displayName'); if (displayName.isDefined()) { ref.module = displayName.getValue(); } ref.module = functionUrlFileName(visitor); } if (ref && !ref.module) { ref.module = '#unknown ' + name; } }); } function registerPathToRoot(refs, registry) { markReactComponentTree(refs, registry); markModules(refs); let breadth = []; forEachRef(refs, (visitor) => { const ref = visitor.getRef(); if (ref.type === 'CallbackGlobalObject') { ref.rootPath = registry.insert(registry.root, ref.type); breadth.push(visitor.clone()); } }); while (breadth.length > 0) { const nextBreadth = []; for (let i = 0; i < breadth.length; i++) { const visitor = breadth[i]; const ref = visitor.getRef(); visitor.forEachEdge((edgeName, edgeVisitor) => { const edgeRef = edgeVisitor.getRef(); if (edgeRef && edgeRef.rootPath === undefined) { let pathName = edgeRef.type; if (edgeName) { pathName = edgeName + ': ' + pathName; } edgeRef.rootPath = registry.insert(ref.rootPath, pathName); nextBreadth.push(edgeVisitor.clone()); // copy module and react tree forward if (edgeRef.module === undefined) { edgeRef.module = ref.module; } if (edgeRef.reactTree === undefined) { edgeRef.reactTree = ref.reactTree; } } }); } breadth = nextBreadth; } } function captureRegistry() { const strings = stringInterner(); const stacks = stackRegistry(strings); const data = new Int32Array(0); const idField = 0; const typeField = 1; const sizeField = 2; const traceField = 3; const pathField = 4; const reactField = 5; const valueField = 6; const moduleField = 7; const numFields = 8; return { strings: strings, stacks: stacks, data: data, register: function registerCapture(captureId, capture) { // NB: capture.refs is potentially VERY large, so we try to avoid making // copies, even of iteration is a bit more annoying. let rowCount = 0; for (const id in capture.refs) { // eslint-disable-line no-unused-vars rowCount++; } for (const id in capture.markedBlocks) { // eslint-disable-line no-unused-vars rowCount++; } console.log( 'increasing row data from ' + (this.data.length * 4).toString() + 'B to ' + (this.data.length * 4 + rowCount * numFields * 4).toString() + 'B' ); const newData = new Int32Array(this.data.length + rowCount * numFields); newData.set(data); let dataOffset = this.data.length; this.data = null; registerPathToRoot(capture.refs, this.stacks); const internedCaptureId = this.strings.intern(captureId); const noneString = this.strings.intern('#none'); const noneStack = this.stacks.insert(this.stacks.root, '#none'); forEachRef(capture.refs, (visitor) => { const ref = visitor.getRef(); const id = visitor.id; newData[dataOffset + idField] = parseInt(id, 16); newData[dataOffset + typeField] = this.strings.intern(ref.type); newData[dataOffset + sizeField] = ref.size; newData[dataOffset + traceField] = internedCaptureId; if (ref.rootPath === undefined) { newData[dataOffset + pathField] = noneStack.id; } else { newData[dataOffset + pathField] = ref.rootPath.id; } if (ref.reactTree === undefined) { newData[dataOffset + reactField] = noneStack.id; } else { newData[dataOffset + reactField] = ref.reactTree.id; } newData[dataOffset + valueField] = this.strings.intern(visitor.getValue()); if (ref.module) { newData[dataOffset + moduleField] = this.strings.intern(ref.module); } else { newData[dataOffset + moduleField] = noneString; } dataOffset += numFields; }); for (const id in capture.markedBlocks) { const block = capture.markedBlocks[id]; newData[dataOffset + idField] = parseInt(id, 16); newData[dataOffset + typeField] = this.strings.intern('Marked Block Overhead'); newData[dataOffset + sizeField] = block.capacity - block.size; newData[dataOffset + traceField] = internedCaptureId; newData[dataOffset + pathField] = noneStack.id; newData[dataOffset + reactField] = noneStack.id; newData[dataOffset + valueField] = this.strings.intern( 'capacity: ' + block.capacity + ', size: ' + block.size + ', granularity: ' + block.cellSize ); newData[dataOffset + moduleField] = noneString; dataOffset += numFields; } this.data = newData; }, getAggrow: function getAggrow() { const agStrings = this.strings; const agStacks = this.stacks.flatten(); const agData = this.data; const agNumRows = agData.length / numFields; const ag = new aggrow(agStrings, agStacks, agNumRows); const idExpander = ag.addFieldExpander('Id', function getId(row) { let id = agData[row * numFields + idField]; if (id < 0) { id += 0x100000000; // data is int32, id is uint32 } return '0x' + id.toString(16); }, function compareAddress(rowA, rowB) { return agData[rowA * numFields + idField] - agData[rowB * numFields + idField]; }); const typeExpander = ag.addFieldExpander('Type', function getType(row) { return agStrings.get(agData[row * numFields + typeField]); }, function compareType(rowA, rowB) { return agData[rowA * numFields + typeField] - agData[rowB * numFields + typeField]; }); const sizeExpander = ag.addFieldExpander('Size', function getSize(row) { return agData[row * numFields + sizeField].toString(); }, function compareSize(rowA, rowB) { return agData[rowA * numFields + sizeField] - agData[rowB * numFields + sizeField]; }); const traceExpander = ag.addFieldExpander('Trace', function getSize(row) { return agStrings.get(agData[row * numFields + traceField]); }, function compareSize(rowA, rowB) { return agData[rowA * numFields + traceField] - agData[rowB * numFields + traceField]; }); const pathExpander = ag.addCalleeStackExpander('Path', function getStack(row) { return agStacks.get(agData[row * numFields + pathField]); }); const reactExpander = ag.addCalleeStackExpander('React Tree', function getStack(row) { return agStacks.get(agData[row * numFields + reactField]); }); const valueExpander = ag.addFieldExpander('Value', function getValue(row) { return agStrings.get(agData[row * numFields + valueField]); }, function compareValue(rowA, rowB) { return agData[rowA * numFields + valueField] - agData[rowB * numFields + valueField]; }); const moduleExpander = ag.addFieldExpander('Module', function getModule(row) { return agStrings.get(agData[row * numFields + moduleField]); }, function compareModule(rowA, rowB) { return agData[rowA * numFields + moduleField] - agData[rowB * numFields + moduleField]; }); const sizeAggregator = ag.addAggregator('Size', function aggregateSize(indices) { let size = 0; for (let i = 0; i < indices.length; i++) { const row = indices[i]; size += agData[row * numFields + sizeField]; } return size; }, function formatSize(value) { return value.toString(); }, function sortSize(a, b) { return b - a; } ); const countAggregator = ag.addAggregator('Count', function aggregateCount(indices) { return indices.length; }, function formatCount(value) { return value.toString(); }, function sortCount(a, b) { return b - a; } ); ag.setActiveExpanders([ pathExpander, reactExpander, moduleExpander, typeExpander, idExpander, traceExpander, valueExpander, sizeExpander ]); ag.setActiveAggregators([sizeAggregator, countAggregator]); return ag; }, }; } if (preLoadedCapture) { const r = new captureRegistry(); r.register('trace', preLoadedCapture); preLoadedCapture = undefined; // let GG clean up the capture ReactDOM.render(<Table aggrow={r.getAggrow()} />, document.body); }