Index: /trunk/src/wp-includes/block-bindings.php
===================================================================
--- /trunk/src/wp-includes/block-bindings.php	(revision 57640)
+++ /trunk/src/wp-includes/block-bindings.php	(revision 57641)
@@ -76,16 +76,17 @@
  *     The array of arguments that are used to register a source.
  *
- *     @type string   $label              The label of the source.
- *     @type callback $get_value_callback A callback executed when the source is processed during block rendering.
- *                                        The callback should have the following signature:
+ *     @type string   $label                   The label of the source.
+ *     @type callback $get_value_callback      A callback executed when the source is processed during block rendering.
+ *                                             The callback should have the following signature:
  *
- *                                        `function ($source_args, $block_instance,$attribute_name): mixed`
- *                                            - @param array    $source_args    Array containing source arguments
- *                                                                              used to look up the override value,
- *                                                                              i.e. {"key": "foo"}.
- *                                            - @param WP_Block $block_instance The block instance.
- *                                            - @param string   $attribute_name The name of an attribute .
- *                                        The callback has a mixed return type; it may return a string to override
- *                                        the block's original value, null, false to remove an attribute, etc.
+ *                                             `function ($source_args, $block_instance,$attribute_name): mixed`
+ *                                                 - @param array    $source_args    Array containing source arguments
+ *                                                                                   used to look up the override value,
+ *                                                                                   i.e. {"key": "foo"}.
+ *                                                 - @param WP_Block $block_instance The block instance.
+ *                                                 - @param string   $attribute_name The name of an attribute .
+ *                                             The callback has a mixed return type; it may return a string to override
+ *                                             the block's original value, null, false to remove an attribute, etc.
+ *     @type array    $uses_context (optional) Array of values to add to block `uses_context` needed by the source.
  * }
  * @return WP_Block_Bindings_Source|false Source when the registration was successful, or `false` on failure.
Index: /trunk/src/wp-includes/block-bindings/pattern-overrides.php
===================================================================
--- /trunk/src/wp-includes/block-bindings/pattern-overrides.php	(revision 57640)
+++ /trunk/src/wp-includes/block-bindings/pattern-overrides.php	(revision 57641)
@@ -40,4 +40,5 @@
 			'label'              => _x( 'Pattern Overrides', 'block bindings source' ),
 			'get_value_callback' => '_block_bindings_pattern_overrides_get_value',
+			'uses_context'       => array( 'pattern/overrides' ),
 		)
 	);
Index: /trunk/src/wp-includes/block-bindings/post-meta.php
===================================================================
--- /trunk/src/wp-includes/block-bindings/post-meta.php	(revision 57640)
+++ /trunk/src/wp-includes/block-bindings/post-meta.php	(revision 57641)
@@ -14,20 +14,18 @@
  * @access private
  *
- * @param array $source_args Array containing source arguments used to look up the override value.
- *                           Example: array( "key" => "foo" ).
+ * @param array    $source_args    Array containing source arguments used to look up the override value.
+ *                                 Example: array( "key" => "foo" ).
+ * @param WP_Block $block_instance The block instance.
  * @return mixed The value computed for the source.
  */
-function _block_bindings_post_meta_get_value( array $source_args ) {
-	if ( ! isset( $source_args['key'] ) ) {
+function _block_bindings_post_meta_get_value( array $source_args, $block_instance ) {
+	if ( empty( $source_args['key'] ) ) {
 		return null;
 	}
 
-	// Use the postId attribute if available.
-	if ( isset( $source_args['postId'] ) ) {
-		$post_id = $source_args['postId'];
-	} else {
-		// $block_instance->context['postId'] is not available in the Image block.
-		$post_id = get_the_ID();
+	if ( empty( $block_instance->context['postId'] ) ) {
+		return null;
 	}
+	$post_id = $block_instance->context['postId'];
 
 	// If a post isn't public, we need to prevent unauthorized users from accessing the post meta.
@@ -52,4 +50,5 @@
 			'label'              => _x( 'Post Meta', 'block bindings source' ),
 			'get_value_callback' => '_block_bindings_post_meta_get_value',
+			'uses_context'       => array( 'postId', 'postType' ),
 		)
 	);
Index: /trunk/src/wp-includes/class-wp-block-bindings-registry.php
===================================================================
--- /trunk/src/wp-includes/class-wp-block-bindings-registry.php	(revision 57640)
+++ /trunk/src/wp-includes/class-wp-block-bindings-registry.php	(revision 57641)
@@ -34,4 +34,29 @@
 
 	/**
+	 * Supported source properties that can be passed to the registered source.
+	 *
+	 * @since 6.5.0
+	 * @var array
+	 */
+	private $allowed_source_properties = array(
+		'label',
+		'get_value_callback',
+		'uses_context',
+	);
+
+	/**
+	 * Supported blocks that can use the block bindings API.
+	 *
+	 * @since 6.5.0
+	 * @var array
+	 */
+	private $supported_blocks = array(
+		'core/paragraph',
+		'core/heading',
+		'core/image',
+		'core/button',
+	);
+
+	/**
 	 * Registers a new block bindings source.
 	 *
@@ -54,16 +79,17 @@
 	 *     The array of arguments that are used to register a source.
 	 *
-	 *     @type string   $label              The label of the source.
-	 *     @type callback $get_value_callback A callback executed when the source is processed during block rendering.
-	 *                                        The callback should have the following signature:
-	 *
-	 *                                        `function ($source_args, $block_instance,$attribute_name): mixed`
-	 *                                            - @param array    $source_args    Array containing source arguments
-	 *                                                                              used to look up the override value,
-	 *                                                                              i.e. {"key": "foo"}.
-	 *                                            - @param WP_Block $block_instance The block instance.
-	 *                                            - @param string   $attribute_name The name of the target attribute.
-	 *                                        The callback has a mixed return type; it may return a string to override
-	 *                                        the block's original value, null, false to remove an attribute, etc.
+	 *     @type string   $label                   The label of the source.
+	 *     @type callback $get_value_callback      A callback executed when the source is processed during block rendering.
+	 *                                             The callback should have the following signature:
+	 *
+	 *                                             `function ($source_args, $block_instance,$attribute_name): mixed`
+	 *                                                 - @param array    $source_args    Array containing source arguments
+	 *                                                                                   used to look up the override value,
+	 *                                                                                   i.e. {"key": "foo"}.
+	 *                                                 - @param WP_Block $block_instance The block instance.
+	 *                                                 - @param string   $attribute_name The name of the target attribute.
+	 *                                             The callback has a mixed return type; it may return a string to override
+	 *                                             the block's original value, null, false to remove an attribute, etc.
+	 *     @type array    $uses_context (optional) Array of values to add to block `uses_context` needed by the source.
 	 * }
 	 * @return WP_Block_Bindings_Source|false Source when the registration was successful, or `false` on failure.
@@ -108,5 +134,5 @@
 		}
 
-		/* Validate that the source properties contain the label */
+		// Validates that the source properties contain the label.
 		if ( ! isset( $source_properties['label'] ) ) {
 			_doing_it_wrong(
@@ -118,5 +144,5 @@
 		}
 
-		/* Validate that the source properties contain the get_value_callback */
+		// Validates that the source properties contain the get_value_callback.
 		if ( ! isset( $source_properties['get_value_callback'] ) ) {
 			_doing_it_wrong(
@@ -128,9 +154,28 @@
 		}
 
-		/* Validate that the get_value_callback is a valid callback */
+		// Validates that the get_value_callback is a valid callback.
 		if ( ! is_callable( $source_properties['get_value_callback'] ) ) {
 			_doing_it_wrong(
 				__METHOD__,
 				__( 'The "get_value_callback" parameter must be a valid callback.' ),
+				'6.5.0'
+			);
+			return false;
+		}
+
+		// Validates that the uses_context parameter is an array.
+		if ( isset( $source_properties['uses_context'] ) && ! is_array( $source_properties['uses_context'] ) ) {
+			_doing_it_wrong(
+				__METHOD__,
+				__( 'The "uses_context" parameter must be an array.' ),
+				'6.5.0'
+			);
+			return false;
+		}
+
+		if ( ! empty( array_diff( array_keys( $source_properties ), $this->allowed_source_properties ) ) ) {
+			_doing_it_wrong(
+				__METHOD__,
+				__( 'The $source_properties array contains invalid properties.' ),
 				'6.5.0'
 			);
@@ -145,4 +190,18 @@
 		$this->sources[ $source_name ] = $source;
 
+		// Adds `uses_context` defined by block bindings sources.
+		add_filter(
+			'get_block_type_uses_context',
+			function ( $uses_context, $block_type ) use ( $source ) {
+				if ( ! in_array( $block_type->name, $this->supported_blocks, true ) || empty( $source->uses_context ) ) {
+					return $uses_context;
+				}
+				// Use array_values to reset the array keys.
+				return array_values( array_unique( array_merge( $uses_context, $source->uses_context ) ) );
+			},
+			10,
+			2
+		);
+
 		return $source;
 	}
Index: /trunk/src/wp-includes/class-wp-block-bindings-source.php
===================================================================
--- /trunk/src/wp-includes/class-wp-block-bindings-source.php	(revision 57640)
+++ /trunk/src/wp-includes/class-wp-block-bindings-source.php	(revision 57641)
@@ -47,4 +47,12 @@
 
 	/**
+	 * The context added to the blocks needed by the source.
+	 *
+	 * @since 6.5.0
+	 * @var array|null
+	 */
+	public $uses_context = null;
+
+	/**
 	 * Constructor.
 	 *
@@ -58,7 +66,8 @@
 	 */
 	public function __construct( string $name, array $source_properties ) {
-		$this->name               = $name;
-		$this->label              = $source_properties['label'];
-		$this->get_value_callback = $source_properties['get_value_callback'];
+		$this->name = $name;
+		foreach ( $source_properties as $property_name => $property_value ) {
+			$this->$property_name = $property_value;
+		}
 	}
 
Index: /trunk/src/wp-includes/class-wp-block-type.php
===================================================================
--- /trunk/src/wp-includes/class-wp-block-type.php	(revision 57640)
+++ /trunk/src/wp-includes/class-wp-block-type.php	(revision 57641)
@@ -181,5 +181,5 @@
 	 * @var string[]
 	 */
-	public $uses_context = array();
+	private $uses_context = array();
 
 	/**
@@ -367,4 +367,8 @@
 		}
 
+		if ( 'uses_context' === $name ) {
+			return $this->get_uses_context();
+		}
+
 		if ( ! in_array( $name, $this->deprecated_properties, true ) ) {
 			return;
@@ -395,5 +399,5 @@
 	 */
 	public function __isset( $name ) {
-		if ( 'variations' === $name ) {
+		if ( in_array( $name, array( 'variations', 'uses_context' ), true ) ) {
 			return true;
 		}
@@ -418,9 +422,4 @@
 	 */
 	public function __set( $name, $value ) {
-		if ( 'variations' === $name ) {
-			$this->variations = $value;
-			return;
-		}
-
 		if ( ! in_array( $name, $this->deprecated_properties, true ) ) {
 			$this->{$name} = $value;
@@ -617,3 +616,22 @@
 		return apply_filters( 'get_block_type_variations', $this->variations, $this );
 	}
+
+	/**
+	 * Get block uses context.
+	 *
+	 * @since 6.5.0
+	 *
+	 * @return array[]
+	 */
+	public function get_uses_context() {
+		/**
+		 * Filters the registered uses context for a block type.
+		 *
+		 * @since 6.5.0
+		 *
+		 * @param array         $uses_context Array of registered uses context for a block type.
+		 * @param WP_Block_Type $block_type   The full block type object.
+		 */
+		return apply_filters( 'get_block_type_uses_context', $this->uses_context, $this );
+	}
 }
Index: /trunk/src/wp-includes/class-wp-block.php
===================================================================
--- /trunk/src/wp-includes/class-wp-block.php	(revision 57640)
+++ /trunk/src/wp-includes/class-wp-block.php	(revision 57641)
@@ -232,11 +232,7 @@
 	 */
 	private function process_block_bindings() {
-		$parsed_block = $this->parsed_block;
-
-		$computed_attributes = array();
-
-		// Allowed blocks that support block bindings.
-		// TODO: Look for a mechanism to opt-in for this. Maybe adding a property to block attributes?
-		$allowed_blocks = array(
+		$parsed_block               = $this->parsed_block;
+		$computed_attributes        = array();
+		$supported_block_attributes = array(
 			'core/paragraph' => array( 'content' ),
 			'core/heading'   => array( 'content' ),
@@ -245,8 +241,8 @@
 		);
 
-		// If the block doesn't have the bindings property, isn't one of the allowed
+		// If the block doesn't have the bindings property, isn't one of the supported
 		// block types, or the bindings property is not an array, return the block content.
 		if (
-			! isset( $allowed_blocks[ $this->name ] ) ||
+			! isset( $supported_block_attributes[ $this->name ] ) ||
 			empty( $parsed_block['attrs']['metadata']['bindings'] ) ||
 			! is_array( $parsed_block['attrs']['metadata']['bindings'] )
@@ -256,6 +252,6 @@
 
 		foreach ( $parsed_block['attrs']['metadata']['bindings'] as $attribute_name => $block_binding ) {
-			// If the attribute is not in the allowed list, process next attribute.
-			if ( ! in_array( $attribute_name, $allowed_blocks[ $this->name ], true ) ) {
+			// If the attribute is not in the supported list, process next attribute.
+			if ( ! in_array( $attribute_name, $supported_block_attributes[ $this->name ], true ) ) {
 				continue;
 			}
Index: /trunk/tests/phpunit/tests/block-bindings/render.php
===================================================================
--- /trunk/tests/phpunit/tests/block-bindings/render.php	(revision 57640)
+++ /trunk/tests/phpunit/tests/block-bindings/render.php	(revision 57641)
@@ -116,4 +116,47 @@
 
 	/**
+	 * Tests passing `uses_context` as argument to the source.
+	 *
+	 * @ticket 60525
+	 *
+	 * @covers ::register_block_bindings_source
+	 */
+	public function test_passing_uses_context_to_source() {
+		$get_value_callback = function ( $source_args, $block_instance, $attribute_name ) {
+			$value = $block_instance->context['sourceContext'];
+			return "Value: $value";
+		};
+
+		register_block_bindings_source(
+			self::SOURCE_NAME,
+			array(
+				'label'              => self::SOURCE_LABEL,
+				'get_value_callback' => $get_value_callback,
+				'uses_context'       => array( 'sourceContext' ),
+			)
+		);
+
+		$block_content = <<<HTML
+<!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"test/source", "args": {"key": "test"}}}}} -->
+<p>This should not appear</p>
+<!-- /wp:paragraph -->
+HTML;
+		$parsed_blocks = parse_blocks( $block_content );
+		$block         = new WP_Block( $parsed_blocks[0], array( 'sourceContext' => 'source context value' ) );
+		$result        = $block->render();
+
+		$this->assertSame(
+			'Value: source context value',
+			$block->attributes['content'],
+			"The 'content' should be updated with the value of the source context."
+		);
+		$this->assertSame(
+			'<p>Value: source context value</p>',
+			trim( $result ),
+			'The block content should be updated with the value of the source context.'
+		);
+	}
+
+	/**
 	 * Tests if the block content is updated with the value returned by the source
 	 * for the Image block in the placeholder state.
Index: /trunk/tests/phpunit/tests/block-bindings/wpBlockBindingsRegistry.php
===================================================================
--- /trunk/tests/phpunit/tests/block-bindings/wpBlockBindingsRegistry.php	(revision 57640)
+++ /trunk/tests/phpunit/tests/block-bindings/wpBlockBindingsRegistry.php	(revision 57641)
@@ -40,4 +40,5 @@
 				return 'test-value';
 			},
+			'uses_context'       => array( 'sourceContext' ),
 		);
 	}
@@ -158,4 +159,21 @@
 
 		self::$test_source_properties['get_value_callback'] = 'not-a-callback';
+
+		$result = $this->registry->register( self::$test_source_name, self::$test_source_properties );
+		$this->assertFalse( $result );
+	}
+
+	/**
+	 * Should reject block bindings registration if `uses_context` is not an array.
+	 *
+	 * @ticket 60525
+	 *
+	 * @covers WP_Block_Bindings_Registry::register
+	 *
+	 * @expectedIncorrectUsage WP_Block_Bindings_Registry::register
+	 */
+	public function test_register_invalid_string_uses_context() {
+
+		self::$test_source_properties['uses_context'] = 'not-an-array';
 
 		$result = $this->registry->register( self::$test_source_name, self::$test_source_properties );
@@ -180,4 +198,11 @@
 			$result
 		);
+		$this->assertSame( 'test/source', $result->name );
+		$this->assertSame( 'Test source', $result->label );
+		$this->assertSame(
+			'test-value',
+			$result->get_value( array(), null, '' )
+		);
+		$this->assertEquals( array( 'sourceContext' ), $result->uses_context );
 	}
 
@@ -322,3 +347,48 @@
 		$this->assertTrue( $result );
 	}
+
+	/**
+	 * Tests merging `uses_context` from multiple sources.
+	 *
+	 * @ticket 60525
+	 *
+	 * @covers ::register_block_bindings_source
+	 * @covers WP_Block_Type::get_uses_context
+	 */
+	public function test_merging_uses_context_from_multiple_sources() {
+		$get_value_callback = function () {
+			return 'Anything';
+		};
+
+		$block_registry        = WP_Block_Type_Registry::get_instance();
+		$original_uses_context = $block_registry->get_registered( 'core/paragraph' )->uses_context;
+
+		register_block_bindings_source(
+			'test/source-one',
+			array(
+				'label'              => 'Test Source One',
+				'get_value_callback' => $get_value_callback,
+				'uses_context'       => array( 'commonContext', 'sourceOneContext' ),
+			)
+		);
+
+		register_block_bindings_source(
+			'test/source-two',
+			array(
+				'label'              => 'Test Source Two',
+				'get_value_callback' => $get_value_callback,
+				'uses_context'       => array( 'commonContext', 'sourceTwoContext' ),
+			)
+		);
+
+		$new_uses_context = $block_registry->get_registered( 'core/paragraph' )->uses_context;
+		// Checks that the resulting `uses_context` contains the values from both sources.
+		$this->assertContains( 'commonContext', $new_uses_context );
+		$this->assertContains( 'sourceOneContext', $new_uses_context );
+		$this->assertContains( 'sourceTwoContext', $new_uses_context );
+		// Checks that the resulting `uses_context` added 3 unique items.
+		$this->assertSame( count( $original_uses_context ) + 3, count( $new_uses_context ) );
+		// Checks that the array isn't sparse to prevent issues in the editor.
+		$this->assertSame( array_key_last( $new_uses_context ), count( $new_uses_context ) - 1 );
+	}
 }
