Skip to content

feat: add folder content provider registry - #1781

Open
wjyrich wants to merge 1 commit into
linuxdeepin:masterfrom
wjyrich:pr/folder-content-provider
Open

wjyrich wants to merge 1 commit into
linuxdeepin:masterfrom
wjyrich:pr/folder-content-provider

Conversation

@wjyrich

@wjyrich wjyrich commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor
  1. Introduce FolderContentRegistry singleton for resolving folder content references
  2. Add FolderPresentation base class for pinned folder icon metadata (title, preview icons, icon/content URLs)
  3. Define FolderContentProvider interface for dynamically loaded plugins
  4. Implement reference encoding/decoding using base64url-encoded provider IDs
  5. Support provider discovery via plugin directories, DDE_SHELL_PLUGIN_PATH, and library paths
  6. Add normalizeReference and referenceWithId helpers for id validation and rebuild
  7. Preserve pins with fallback presentation when a provider is unavailable
  8. Register new source/header files in CMakeLists.txt

Log: Added folder content provider plugin system for custom folder pins on the taskbar/dock

Influence:

  1. Test reference encoding and decoding with valid and invalid inputs
  2. Verify provider loading from all search paths (plugins dir, install dir, DDE_SHELL_PLUGIN_PATH, library paths)
  3. Test normalizeReference behavior with available and missing providers
  4. Verify fallback FolderPresentation is created when provider is missing
  5. Test that pins are retained when provider is temporarily unavailable
  6. Validate base64url ID encoding handles special characters and empty IDs correctly
  7. Confirm source pattern validation rejects malformed references
  8. Test referenceWithId rebuilds references while preserving source

feat: 添加文件夹内容提供者注册表

  1. 引入 FolderContentRegistry 单例,用于解析文件夹内容引用
  2. 添加 FolderPresentation 基类,用于固定文件夹图标的元数据(标题、预览 图标、图标/内容 URL)
  3. 定义 FolderContentProvider 接口,用于动态加载插件
  4. 实现基于 base64url 编码提供者 ID 的引用编解码
  5. 支持通过插件目录、DDE_SHELL_PLUGIN_PATH 和库路径发现提供者
  6. 添加 normalizeReference 和 referenceWithId 辅助方法,用于 ID 校验和 重建
  7. 当提供者不可用时,通过回退展示保留固定项
  8. 在 CMakeLists.txt 中注册新的源文件和头文件

Log: 新增文件夹内容提供者插件系统,支持任务栏/停靠栏自定义文件夹固定项

Influence:

  1. 使用有效和无效输入测试引用编解码
  2. 验证从所有搜索路径(插件目录、安装目录、DDE_SHELL_PLUGIN_PATH、库路 径)加载提供者
  3. 测试在提供者可用和缺失情况下 normalizeReference 的行为
  4. 验证提供者缺失时创建回退的 FolderPresentation
  5. 测试提供者临时不可用时固定项是否保留
  6. 验证 base64url ID 编码正确处理特殊字符和空 ID
  7. 确认 source 模式校验拒绝格式错误的引用
  8. 测试 referenceWithId 在保留 source 的同时重建引用

PMS: TASK-397253

Summary by Sourcery

Add extensible folder content provider support for custom taskbar and dock folder pins.

New Features:

  • Add a plugin-based folder content provider system for resolving and presenting custom pinned folder references.

Bug Fixes:

  • Preserve pinned folder items with a fallback presentation when their content provider is unavailable.

Enhancements:

  • Support validated folder reference encoding, decoding, normalization, and provider-ID replacement.
  • Discover folder content providers from bundled plugin directories, environment-configured paths, and library paths.
  • Expose folder presentation metadata for titles, preview icons, and icon or content URLs.

Build:

  • Register the folder content provider sources and headers with the frame library build.

1. Introduce FolderContentRegistry singleton for resolving folder
content references
2. Add FolderPresentation base class for pinned folder icon metadata
(title, preview icons, icon/content URLs)
3. Define FolderContentProvider interface for dynamically loaded plugins
4. Implement reference encoding/decoding using base64url-encoded
provider IDs
5. Support provider discovery via plugin directories,
DDE_SHELL_PLUGIN_PATH, and library paths
6. Add normalizeReference and referenceWithId helpers for id validation
and rebuild
7. Preserve pins with fallback presentation when a provider is
unavailable
8. Register new source/header files in CMakeLists.txt

Log: Added folder content provider plugin system for custom folder pins
on the taskbar/dock

Influence:
1. Test reference encoding and decoding with valid and invalid inputs
2. Verify provider loading from all search paths (plugins dir, install
dir, DDE_SHELL_PLUGIN_PATH, library paths)
3. Test normalizeReference behavior with available and missing providers
4. Verify fallback FolderPresentation is created when provider is
missing
5. Test that pins are retained when provider is temporarily unavailable
6. Validate base64url ID encoding handles special characters and empty
IDs correctly
7. Confirm source pattern validation rejects malformed references
8. Test referenceWithId rebuilds references while preserving source

feat: 添加文件夹内容提供者注册表

1. 引入 FolderContentRegistry 单例,用于解析文件夹内容引用
2. 添加 FolderPresentation 基类,用于固定文件夹图标的元数据(标题、预览
图标、图标/内容 URL)
3. 定义 FolderContentProvider 接口,用于动态加载插件
4. 实现基于 base64url 编码提供者 ID 的引用编解码
5. 支持通过插件目录、DDE_SHELL_PLUGIN_PATH 和库路径发现提供者
6. 添加 normalizeReference 和 referenceWithId 辅助方法,用于 ID 校验和
重建
7. 当提供者不可用时,通过回退展示保留固定项
8. 在 CMakeLists.txt 中注册新的源文件和头文件

Log: 新增文件夹内容提供者插件系统,支持任务栏/停靠栏自定义文件夹固定项

Influence:
1. 使用有效和无效输入测试引用编解码
2. 验证从所有搜索路径(插件目录、安装目录、DDE_SHELL_PLUGIN_PATH、库路
径)加载提供者
3. 测试在提供者可用和缺失情况下 normalizeReference 的行为
4. 验证提供者缺失时创建回退的 FolderPresentation
5. 测试提供者临时不可用时固定项是否保留
6. 验证 base64url ID 编码正确处理特殊字符和空 ID
7. 确认 source 模式校验拒绝格式错误的引用
8. 测试 referenceWithId 在保留 source 的同时重建引用

PMS: TASK-397253
@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: wjyrich

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai

sourcery-ai Bot commented Oct 27, 2026

Copy link
Copy Markdown

Reviewer's Guide

Introduces a singleton folder-content registry and plugin contract that encodes and validates folder references, discovers and caches providers across configured plugin paths, normalizes provider IDs, and preserves unavailable pins through fallback presentations.

Sequence diagram for folder reference creation and presentation resolution

sequenceDiagram
    participant Client
    participant Registry as FolderContentRegistry
    participant Provider as FolderContentProvider
    participant Presentation as FolderPresentation

    Client->>Registry: normalizeReference(reference)
    Registry->>Registry: decode(reference, source, id)
    Registry->>Registry: provider(source)
    Registry->>Provider: normalizeId(id)
    Provider-->>Registry: normalizedId
    Registry-->>Client: reference(source, normalizedId)

    Client->>Registry: create(reference, parent)
    Registry->>Registry: decode(reference, source, id)
    Registry->>Provider: create(id, parent)
    Provider-->>Registry: FolderPresentation
    Registry-->>Client: FolderPresentation
Loading

Sequence diagram for provider discovery and unavailable pin fallback

sequenceDiagram
    participant Registry as FolderContentRegistry
    participant PluginLoader as QPluginLoader
    participant Provider as FolderContentProvider
    participant Pin as FolderPresentation

    Registry->>Registry: provider(source)
    Registry->>PluginLoader: instance()
    PluginLoader-->>Registry: plugin object
    Registry->>Provider: qobject_cast<FolderContentProvider>()
    Provider-->>Registry: provider instance
    Registry->>Provider: create(id, parent)
    Provider-->>Registry: FolderPresentation

    alt provider unavailable
        Registry->>Pin: new FolderPresentation(parent)
        Pin-->>Registry: fallback presentation
    end
Loading

Flow diagram for folder reference validation and normalization

flowchart TD
    A[Folder reference] --> B[decode]
    B -->|invalid format or encoding| X[Return empty or false]
    B -->|valid source and non-empty ID| C["provider(source)"]
    C -->|available| D[normalizeId]
    D -->|normalized ID| E["reference(source, normalized ID)"]
    E --> F[Normalized reference]
    C -->|unavailable| X
Loading

File-Level Changes

Change Details Files
Added the folder content provider API and presentation metadata model for dynamically supplied pinned-folder content.
  • Introduced QObject-based presentation metadata with title, preview icons, and icon/content URLs.
  • Defined the plugin provider interface and Qt interface identifier.
  • Exported the new public header and compiled implementation through CMake.
frame/foldercontent.h
frame/CMakeLists.txt
Implemented singleton registry logic for validating, encoding, decoding, normalizing, and rebuilding folder references.
  • Encoded provider IDs with unpadded base64url and validated canonical references and source names.
  • Added provider-aware normalization and source-preserving reference reconstruction.
  • Created presentations through the resolved provider or returned a fallback presentation for unavailable providers.
frame/foldercontent.cpp
frame/foldercontent.h
Implemented dynamic provider discovery across shell plugin and runtime library search paths while caching successful providers.
  • Resolved provider libraries using the standard filename convention.
  • Searched the application plugin directory, install directory, DDE_SHELL_PLUGIN_PATH, and Qt library paths.
  • Loaded providers with QPluginLoader, checked interface compatibility, cached instances, and logged failures.
frame/foldercontent.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="frame/foldercontent.cpp" line_range="22" />
<code_context>
+
+QString FolderContentRegistry::reference(const QString &source, const QString &id)
+{
+    return QStringLiteral("folder/%1/%2").arg(source, QString::fromLatin1(id.toUtf8().toBase64(
+        QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)));
+}
</code_context>
<issue_to_address>
**Generated folder reference is unusable**

When a caller passes an empty ID or a source that fails the accepted pattern, `reference()` encodes the arguments without checking the decoder's constraints, so `decode()` rejects the returned value and callers cannot use it with `isReference()` or `create()`.

Make `reference()` reject empty IDs and sources that fail the decoder's pattern before constructing a reference.
</issue_to_address>

### Comment 2
<location path="frame/foldercontent.cpp" line_range="54-79" />
<code_context>
+    if (m_providers.contains(source))
</code_context>
<issue_to_address>
**Concurrent lookups race on providers**

When two threads call `create()` or `normalizeReference()` on the singleton at the same time, `provider()` checks and updates the shared `m_providers` hash without synchronization, so concurrent `create()` or `normalizeReference()` calls can race while loading and inserting providers, corrupting the cache or returning inconsistent results. Constructing a `QPluginLoader` with the registry as parent from a non-owner thread also violates QObject thread-affinity rules.

Serialize registry operations on the registry's owning thread, or protect provider lookup/loading and cache updates with an appropriate synchronization strategy.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread frame/foldercontent.cpp

QString FolderContentRegistry::reference(const QString &source, const QString &id)
{
return QStringLiteral("folder/%1/%2").arg(source, QString::fromLatin1(id.toUtf8().toBase64(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Generated folder reference is unusable

When a caller passes an empty ID or a source that fails the accepted pattern, reference() encodes the arguments without checking the decoder's constraints, so decode() rejects the returned value and callers cannot use it with isReference() or create().

Make reference() reject empty IDs and sources that fail the decoder's pattern before constructing a reference.

Prompt for AI agents
In `frame/foldercontent.cpp` at line 22:

**Generated folder reference is unusable**

When a caller passes an empty ID or a source that fails the accepted pattern, `reference()` encodes the arguments without checking the decoder's constraints, so `decode()` rejects the returned value and callers cannot use it with `isReference()` or `create()`.

Make `reference()` reject empty IDs and sources that fail the decoder's pattern before constructing a reference.

Comment thread frame/foldercontent.cpp
Comment on lines +54 to +79
if (m_providers.contains(source))
return m_providers.value(source);

// Content providers live in the regular shell plugin directory and follow
// the same file naming as every other shell plugin:
// org.deepin.ds.folderprovider.<source>.so
const auto fileName = QStringLiteral("org.deepin.ds.folderprovider.%1.so").arg(source);
QStringList dirs {
QDir(QCoreApplication::applicationDirPath()).absoluteFilePath("../plugins"),
QStringLiteral(DDE_SHELL_PLUGIN_INSTALL_DIR)
};
const auto runtimePaths = qEnvironmentVariable("DDE_SHELL_PLUGIN_PATH");
if (!runtimePaths.isEmpty())
dirs.append(runtimePaths.split(QDir::listSeparator(), Qt::SkipEmptyParts));
dirs.append(QCoreApplication::libraryPaths());

for (const auto &dir : dirs) {
const auto path = QDir(dir).filePath(fileName);
if (!QFileInfo::exists(path))
continue;
auto loader = new QPluginLoader(path, this);
auto object = loader->instance();
auto contentProvider = qobject_cast<FolderContentProvider *>(object);
if (contentProvider) {
m_providers.insert(source, contentProvider);
return contentProvider;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Concurrent lookups race on providers

When two threads call create() or normalizeReference() on the singleton at the same time, provider() checks and updates the shared m_providers hash without synchronization, so concurrent create() or normalizeReference() calls can race while loading and inserting providers, corrupting the cache or returning inconsistent results. Constructing a QPluginLoader with the registry as parent from a non-owner thread also violates QObject thread-affinity rules.

Serialize registry operations on the registry's owning thread, or protect provider lookup/loading and cache updates with an appropriate synchronization strategy.

Prompt for AI agents
In `frame/foldercontent.cpp` at lines 54-79:

**Concurrent lookups race on providers**

When two threads call `create()` or `normalizeReference()` on the singleton at the same time, `provider()` checks and updates the shared `m_providers` hash without synchronization, so concurrent `create()` or `normalizeReference()` calls can race while loading and inserting providers, corrupting the cache or returning inconsistent results. Constructing a `QPluginLoader` with the registry as parent from a non-owner thread also violates QObject thread-affinity rules.

Serialize registry operations on the registry's owning thread, or protect provider lookup/loading and cache updates with an appropriate synchronization strategy.

@deepin-bot

deepin-bot Bot commented Oct 28, 2026

Copy link
Copy Markdown

TAG Bot

New tag: 2.0.61
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #1786

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants