blob: 13afd633f4639dc2ff76266dfe7bf67165165836 [file] [log] [blame]
// Copyright 2017 PDFium 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 "core/fpdfapi/parser/cpdf_object_avail.h"
#include <utility>
#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
#include "core/fpdfapi/parser/cpdf_object_walker.h"
#include "core/fpdfapi/parser/cpdf_read_validator.h"
#include "core/fpdfapi/parser/cpdf_reference.h"
#include "third_party/base/check.h"
#include "third_party/base/containers/contains.h"
CPDF_ObjectAvail::CPDF_ObjectAvail(
const RetainPtr<CPDF_ReadValidator>& validator,
CPDF_IndirectObjectHolder* holder,
CPDF_Object* root)
: validator_(validator), holder_(holder), root_(root) {
DCHECK(validator_);
DCHECK(holder);
DCHECK(root_);
if (!root_->IsInline())
parsed_objnums_.insert(root_->GetObjNum());
}
CPDF_ObjectAvail::CPDF_ObjectAvail(
const RetainPtr<CPDF_ReadValidator>& validator,
CPDF_IndirectObjectHolder* holder,
uint32_t obj_num)
: validator_(validator),
holder_(holder),
root_(pdfium::MakeRetain<CPDF_Reference>(holder, obj_num)) {
DCHECK(validator_);
DCHECK(holder);
}
CPDF_ObjectAvail::~CPDF_ObjectAvail() = default;
CPDF_DataAvail::DocAvailStatus CPDF_ObjectAvail::CheckAvail() {
if (!LoadRootObject())
return CPDF_DataAvail::kDataNotAvailable;
if (CheckObjects()) {
CleanMemory();
return CPDF_DataAvail::kDataAvailable;
}
return CPDF_DataAvail::kDataNotAvailable;
}
bool CPDF_ObjectAvail::LoadRootObject() {
if (!non_parsed_objects_.empty())
return true;
while (root_ && root_->IsReference()) {
const uint32_t ref_obj_num = root_->AsReference()->GetRefObjNum();
if (HasObjectParsed(ref_obj_num)) {
root_ = nullptr;
return true;
}
CPDF_ReadValidator::ScopedSession parse_session(validator_);
CPDF_Object* direct = holder_->GetOrParseIndirectObject(ref_obj_num);
if (validator_->has_read_problems())
return false;
parsed_objnums_.insert(ref_obj_num);
root_.Reset(direct);
}
std::stack<uint32_t> non_parsed_objects_in_root;
if (AppendObjectSubRefs(root_.Get(), &non_parsed_objects_in_root)) {
non_parsed_objects_ = std::move(non_parsed_objects_in_root);
return true;
}
return false;
}
bool CPDF_ObjectAvail::CheckObjects() {
std::set<uint32_t> checked_objects;
std::stack<uint32_t> objects_to_check = std::move(non_parsed_objects_);
non_parsed_objects_ = std::stack<uint32_t>();
while (!objects_to_check.empty()) {
const uint32_t obj_num = objects_to_check.top();
objects_to_check.pop();
if (HasObjectParsed(obj_num))
continue;
if (!checked_objects.insert(obj_num).second)
continue;
CPDF_ReadValidator::ScopedSession parse_session(validator_);
const CPDF_Object* direct = holder_->GetOrParseIndirectObject(obj_num);
if (direct == root_)
continue;
if (validator_->has_read_problems() ||
!AppendObjectSubRefs(direct, &objects_to_check)) {
non_parsed_objects_.push(obj_num);
continue;
}
parsed_objnums_.insert(obj_num);
}
return non_parsed_objects_.empty();
}
bool CPDF_ObjectAvail::AppendObjectSubRefs(const CPDF_Object* object,
std::stack<uint32_t>* refs) const {
DCHECK(refs);
if (!object)
return true;
CPDF_ObjectWalker walker(object);
while (const CPDF_Object* obj = walker.GetNext()) {
CPDF_ReadValidator::ScopedSession parse_session(validator_);
// Skip if this object if it's an inlined root, the parent object or
// explicitily excluded.
const bool skip = (walker.GetParent() && obj == root_) ||
walker.dictionary_key() == "Parent" ||
(obj != root_ && ExcludeObject(obj));
// We need to parse the object before we can do the exclusion check.
// This is because the exclusion check may check against a referenced
// field of the object which we need to make sure is loaded.
if (validator_->has_read_problems())
return false;
if (skip) {
walker.SkipWalkIntoCurrentObject();
continue;
}
if (obj->IsReference())
refs->push(obj->AsReference()->GetRefObjNum());
}
return true;
}
void CPDF_ObjectAvail::CleanMemory() {
root_.Reset();
parsed_objnums_.clear();
}
bool CPDF_ObjectAvail::ExcludeObject(const CPDF_Object* object) const {
return false;
}
bool CPDF_ObjectAvail::HasObjectParsed(uint32_t obj_num) const {
return pdfium::Contains(parsed_objnums_, obj_num);
}